update external headers

This commit is contained in:
Nick Fisher
2022-02-06 13:28:28 +08:00
parent 24d0973129
commit a08f3d95e3
150 changed files with 27445 additions and 14805 deletions

View File

@@ -0,0 +1,57 @@
cmake_minimum_required(VERSION 3.19)
project(camutils)
set(TARGET camutils)
set(PUBLIC_HDR_DIR include)
# ==================================================================================================
# Sources and headers
# ==================================================================================================
set(PUBLIC_HDRS
include/camutils/Bookmark.h
include/camutils/compiler.h
include/camutils/Manipulator.h
)
set(SRCS
src/Bookmark.cpp
src/FreeFlightManipulator.h
src/Manipulator.cpp
src/MapManipulator.h
src/OrbitManipulator.h
)
# ==================================================================================================
# Include and target definitions
# ==================================================================================================
include_directories(${PUBLIC_HDR_DIR})
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
target_link_libraries(${TARGET} PUBLIC math)
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
# ==================================================================================================
# Compiler flags
# ==================================================================================================
if (MSVC)
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:/fp:fast>)
else()
target_compile_options(${TARGET} PRIVATE $<$<CONFIG:Release>:-ffast-math>)
target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register)
endif()
# ==================================================================================================
# Installation
# ==================================================================================================
install(TARGETS ${TARGET} ARCHIVE DESTINATION lib/${DIST_DIR})
install(DIRECTORY ${PUBLIC_HDR_DIR}/camutils DESTINATION include)
# ==================================================================================================
# Tests
# ==================================================================================================
if (NOT ANDROID AND NOT WEBGL AND NOT IOS)
add_executable(test_${TARGET} tests/test_camutils.cpp)
target_link_libraries(test_${TARGET} PRIVATE camutils gtest)
endif()

View File

@@ -27,6 +27,8 @@
namespace filament::backend {
class CallbackHandler;
/**
* A CPU memory-buffer descriptor, typically used to transfer data from the CPU to the GPU.
*
@@ -53,8 +55,8 @@ public:
//! calls the callback to advertise BufferDescriptor no-longer owns the buffer
~BufferDescriptor() noexcept {
if (callback) {
callback(buffer, size, user);
if (mCallback) {
mCallback(buffer, size, mUser);
}
}
@@ -62,19 +64,21 @@ public:
BufferDescriptor& operator=(const BufferDescriptor& rhs) = delete;
BufferDescriptor(BufferDescriptor&& rhs) noexcept
: buffer(rhs.buffer), size(rhs.size), callback(rhs.callback), user(rhs.user) {
: buffer(rhs.buffer), size(rhs.size),
mCallback(rhs.mCallback), mUser(rhs.mUser), mHandler(rhs.mHandler) {
rhs.buffer = nullptr;
rhs.callback = nullptr;
rhs.mCallback = nullptr;
}
BufferDescriptor& operator=(BufferDescriptor&& rhs) noexcept {
if (this != &rhs) {
buffer = rhs.buffer;
size = rhs.size;
callback = rhs.callback;
user = rhs.user;
mCallback = rhs.mCallback;
mUser = rhs.mUser;
mHandler = rhs.mHandler;
rhs.buffer = nullptr;
rhs.callback = nullptr;
rhs.mCallback = nullptr;
}
return *this;
}
@@ -88,7 +92,20 @@ public:
*/
BufferDescriptor(void const* buffer, size_t size,
Callback callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), callback(callback), user(user) {
: buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) {
}
/**
* Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t size,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size),
mCallback(callback), mUser(user), mHandler(handler) {
}
// --------------------------------------------------------------------------------------------
@@ -100,14 +117,15 @@ public:
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return a new BufferDescriptor
*/
template<typename T, void(T::*method)(void const* buffer, size_t size)>
template<typename T, void(T::*method)(void const*, size_t)>
static BufferDescriptor make(
void const* buffer, size_t size, T* data) noexcept {
void const* buffer, size_t size, T* data, CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
[](void* b, size_t s, void* u) {
handler, [](void* b, size_t s, void* u) {
(*static_cast<T**>(u)->*method)(b, s);
}, data
};
@@ -123,14 +141,15 @@ public:
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param functor functor of type f(void const* buffer, size_t size)
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return a new BufferDescriptor
*/
template<typename T>
static BufferDescriptor make(
void const* buffer, size_t size, T&& functor) noexcept {
void const* buffer, size_t size, T&& functor, CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
[](void* b, size_t s, void* u) {
handler, [](void* b, size_t s, void* u) {
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
@@ -147,21 +166,39 @@ public:
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(Callback callback, void* user = nullptr) noexcept {
this->callback = callback;
this->user = user;
this->mCallback = callback;
this->mUser = user;
this->mHandler = nullptr;
}
/**
* Set or replace the release callback function
* @param handler The Handler to use to dispatch the callback
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept {
mCallback = callback;
mUser = user;
mHandler = handler;
}
//! Returns whether a release callback is set
bool hasCallback() const noexcept { return callback != nullptr; }
bool hasCallback() const noexcept { return mCallback != nullptr; }
//! Returns the currently set release callback function
Callback getCallback() const noexcept {
return callback;
return mCallback;
}
//! Returns the handler for this callback or nullptr if the default handler is to be used.
CallbackHandler* getHandler() const noexcept {
return mHandler;
}
//! Returns the user opaque pointer associated to this BufferDescriptor
void* getUser() const noexcept {
return user;
return mUser;
}
//! CPU mempry-buffer virtual address
@@ -172,8 +209,9 @@ public:
private:
// callback when the buffer is consumed.
Callback callback = nullptr;
void* user = nullptr;
Callback mCallback = nullptr;
void* mUser = nullptr;
CallbackHandler* mHandler = nullptr;
};
} // namespace filament::backend

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
#define TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
#include <utils/compiler.h>
namespace filament::backend {
/**
* A generic interface to dispatch callbacks.
*
* All APIs that take a callback as argument also take a
* CallbackHandler* which is used to dispatch the
* callback: CallbackHandler::post() method is called from a service thread as soon
* as possible (this will NEVER be the main thread), CallbackHandler::post()
* is responsible for scheduling the callback onto the thread the
* user desires.
*
* This is intended to make callbacks interoperate with
* the platform/OS's own messaging system.
*
* CallbackHandler* can always be nullptr in which case the default handler is used. The
* default handler always dispatches callbacks on filament's main thread opportunistically.
*
* Life time:
* ---------
*
* Filament make no attempts to manage the life time of the CallbackHandler* and never takes
* ownership.
* In particular, this means that the CallbackHandler instance must stay valid until all
* pending callbacks are been dispatched.
*
* Similarly, when shutting down filament, care must be taken to ensure that all pending callbacks
* that might access filament's state have been dispatched. Filament can no longer ensure this
* because callback execution is the responsibility of the CallbackHandler, which is external to
* filament.
* Typically, the concrete CallbackHandler would have a mechanism to drain and/or wait for all
* callbacks to be processed.
*
*/
class CallbackHandler {
public:
using Callback = void(*)(void* user);
/**
* Schedules the callback to be called onto the appropriate thread.
* Typically this will be the application's main thead.
*
* Must be thread-safe.
*/
virtual void post(void* user, Callback callback) = 0;
protected:
virtual ~CallbackHandler();
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H

View File

@@ -193,10 +193,12 @@ static constexpr size_t SHADER_MODEL_COUNT = 3;
*/
enum class PrimitiveType : uint8_t {
// don't change the enums values (made to match GL)
POINTS = 0, //!< points
LINES = 1, //!< lines
TRIANGLES = 4, //!< triangles
NONE = 0xFF
POINTS = 0, //!< points
LINES = 1, //!< lines
LINE_STRIP = 3, //!< line strip
TRIANGLES = 4, //!< triangles
TRIANGLE_STRIP = 5, //!< triangle strip
NONE = 0xFF
};
/**
@@ -220,7 +222,8 @@ enum class UniformType : uint8_t {
UINT3,
UINT4,
MAT3, //!< a 3x3 float matrix
MAT4 //!< a 4x4 float matrix
MAT4, //!< a 4x4 float matrix
STRUCT
};
enum class Precision : uint8_t {
@@ -806,10 +809,10 @@ using AttributeArray = std::array<Attribute, MAX_VERTEX_ATTRIBUTE_COUNT>;
//! Raster state descriptor
struct RasterState {
using CullingMode = CullingMode;
using DepthFunc = SamplerCompareFunc;
using BlendEquation = BlendEquation;
using BlendFunction = BlendFunction;
using CullingMode = backend::CullingMode;
using DepthFunc = backend::SamplerCompareFunc;
using BlendEquation = backend::BlendEquation;
using BlendFunction = backend::BlendFunction;
RasterState() noexcept { // NOLINT
static_assert(sizeof(RasterState) == sizeof(uint32_t),

View File

@@ -21,6 +21,8 @@
#include <utils/Log.h>
#include <utils/debug.h>
#include <limits>
namespace filament {
namespace backend {

View File

@@ -28,8 +28,7 @@
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace backend {
namespace filament::backend {
/**
* A descriptor to an image in main memory, typically used to transfer image data from the CPU
@@ -58,9 +57,19 @@ public:
* @param left Left coordinate in pixels
* @param top Top coordinate in pixels
* @param stride Stride of a row in pixels
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment,
uint32_t left, uint32_t top, uint32_t stride,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, handler, callback, user),
left(left), top(top), stride(stride),
format(format), type(type), alignment(alignment) {
}
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment = 1,
uint32_t left = 0, uint32_t top = 0, uint32_t stride = 0,
@@ -77,9 +86,17 @@ public:
* @param size Size in bytes of the buffer containing the image
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, handler, callback, user),
stride(0), format(format), type(type), alignment(1) {
}
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type,
Callback callback, void* user = nullptr) noexcept
@@ -87,6 +104,7 @@ public:
stride(0), format(format), type(type), alignment(1) {
}
/**
* Creates a new PixelBufferDescriptor referencing a compressed image in main memory
*
@@ -94,9 +112,18 @@ public:
* @param size Size in bytes of the buffer containing the image
* @param format Compressed format of the image
* @param imageSize Compressed size of the image
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, handler, callback, user),
imageSize(imageSize), compressedFormat(format), type(PixelDataType::COMPRESSED),
alignment(1) {
}
PixelBufferDescriptor(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize,
Callback callback, void* user = nullptr) noexcept
@@ -107,26 +134,29 @@ public:
// --------------------------------------------------------------------------------------------
template<typename T, void(T::*method)(void const* buffer, size_t size)>
template<typename T, void(T::*method)(void const*, size_t)>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment,
uint32_t left, uint32_t top, uint32_t stride, T* data) noexcept {
uint32_t left, uint32_t top, uint32_t stride, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
[](void* b, size_t s, void* u) {
handler, [](void* b, size_t s, void* u) {
(*static_cast<T**>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const* buffer, size_t size)>
template<typename T, void(T::*method)(void const*, size_t)>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, T* data) noexcept {
return { buffer, size, format, type, [](void* b, size_t s, void* u) {
PixelDataFormat format, PixelDataType type, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) {
(*static_cast<T**>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const* buffer, size_t size)>
template<typename T, void(T::*method)(void const*, size_t)>
static PixelBufferDescriptor make(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize, T* data) noexcept {
return { buffer, size, format, imageSize, [](void* b, size_t s, void* u) {
backend::CompressedPixelDataType format, uint32_t imageSize, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) {
(*static_cast<T**>(u)->*method)(b, s); }, data
};
}
@@ -134,9 +164,10 @@ public:
template<typename T>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment,
uint32_t left, uint32_t top, uint32_t stride, T&& functor) noexcept {
uint32_t left, uint32_t top, uint32_t stride, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
[](void* b, size_t s, void* u) {
handler, [](void* b, size_t s, void* u) {
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
@@ -146,9 +177,10 @@ public:
template<typename T>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, T&& functor) noexcept {
PixelDataFormat format, PixelDataType type, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type,
[](void* b, size_t s, void* u) {
handler, [](void* b, size_t s, void* u) {
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
@@ -158,9 +190,10 @@ public:
template<typename T>
static PixelBufferDescriptor make(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize, T&& functor) noexcept {
backend::CompressedPixelDataType format, uint32_t imageSize, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize,
[](void* b, size_t s, void* u) {
handler, [](void* b, size_t s, void* u) {
T& that = *static_cast<T*>(u);
that(b, s);
delete &that;
@@ -275,8 +308,7 @@ public:
uint8_t alignment : 4;
};
} // namespace backend
} // namespace filament
} // namespace backend::filament
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::PixelBufferDescriptor& b);

6552
ios/include/cgltf.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CallbackUtils.h"
#include "private/backend/VirtualMachineEnv.h"
void acquireCallbackJni(JNIEnv* env, CallbackJni& callbackUtils) {
#ifdef __ANDROID__
callbackUtils.handlerClass = env->FindClass("android/os/Handler");
callbackUtils.handlerClass = (jclass) env->NewGlobalRef(callbackUtils.handlerClass);
callbackUtils.post = env->GetMethodID(callbackUtils.handlerClass,
"post", "(Ljava/lang/Runnable;)Z");
#endif
callbackUtils.executorClass = env->FindClass("java/util/concurrent/Executor");
callbackUtils.executorClass = (jclass) env->NewGlobalRef(callbackUtils.executorClass);
callbackUtils.execute = env->GetMethodID(callbackUtils.executorClass,
"execute", "(Ljava/lang/Runnable;)V");
}
void releaseCallbackJni(JNIEnv* env, CallbackJni callbackUtils, jobject handler, jobject callback) {
if (handler && callback) {
#ifdef __ANDROID__
if (env->IsInstanceOf(handler, callbackUtils.handlerClass)) {
env->CallBooleanMethod(handler, callbackUtils.post, callback);
}
#endif
if (env->IsInstanceOf(handler, callbackUtils.executorClass)) {
env->CallVoidMethod(handler, callbackUtils.execute, callback);
}
}
env->DeleteGlobalRef(handler);
env->DeleteGlobalRef(callback);
#ifdef __ANDROID__
env->DeleteGlobalRef(callbackUtils.handlerClass);
#endif
env->DeleteGlobalRef(callbackUtils.executorClass);
}
// -----------------------------------------------------------------------------------------------
JniCallback* JniCallback::make(JNIEnv* env, jobject handler, jobject callback) {
return new JniCallback(env, handler, callback);
}
JniCallback::JniCallback(JNIEnv* env, jobject handler, jobject callback)
: mHandler(env->NewGlobalRef(handler)),
mCallback(env->NewGlobalRef(callback)) {
acquireCallbackJni(env, mCallbackUtils);
}
JniCallback::~JniCallback() = default;
void JniCallback::post(void* user, filament::backend::CallbackHandler::Callback callback) {
callback(user);
}
void JniCallback::postToJavaAndDestroy(JniCallback* callback) {
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
releaseCallbackJni(env, callback->mCallbackUtils, callback->mHandler, callback->mCallback);
delete callback;
}
// -----------------------------------------------------------------------------------------------
JniBufferCallback* JniBufferCallback::make(filament::Engine*,
JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer) {
return new JniBufferCallback(env, handler, callback, std::move(buffer));
}
JniBufferCallback::JniBufferCallback(JNIEnv* env, jobject handler, jobject callback,
AutoBuffer&& buffer)
: JniCallback(env, handler, callback),
mBuffer(std::move(buffer)) {
}
JniBufferCallback::~JniBufferCallback() = default;
void JniBufferCallback::postToJavaAndDestroy(void*, size_t, void* user) {
JniBufferCallback* callback = (JniBufferCallback*)user;
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
callback->mBuffer.attachToJniThread(env);
releaseCallbackJni(env, callback->mCallbackUtils, callback->mHandler, callback->mCallback);
delete callback;
}
// -----------------------------------------------------------------------------------------------
JniImageCallback* JniImageCallback::make(filament::Engine*,
JNIEnv* env, jobject handler, jobject callback, long image) {
return new JniImageCallback(env, handler, callback, image);
}
JniImageCallback::JniImageCallback(JNIEnv* env, jobject handler, jobject callback, long image)
: JniCallback(env, handler, callback),
mImage(image) {
}
JniImageCallback::~JniImageCallback() = default;
void JniImageCallback::postToJavaAndDestroy(void*, void* user) {
JniImageCallback* callback = (JniImageCallback*)user;
JNIEnv* env = filament::VirtualMachineEnv::get().getEnvironment();
releaseCallbackJni(env, callback->mCallbackUtils, callback->mHandler, callback->mCallback);
delete callback;
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <jni.h>
#include "common/NioUtils.h"
#include <backend/CallbackHandler.h>
#include <filament/Engine.h>
struct CallbackJni {
#ifdef __ANDROID__
jclass handlerClass = nullptr;
jmethodID post = nullptr;
#endif
jclass executorClass = nullptr;
jmethodID execute = nullptr;
};
void acquireCallbackJni(JNIEnv* env, CallbackJni& callbackUtils);
void releaseCallbackJni(JNIEnv* env, CallbackJni callbackUtils, jobject handler, jobject callback);
struct JniCallback : private filament::backend::CallbackHandler {
JniCallback(JniCallback const &) = delete;
JniCallback(JniCallback&&) = delete;
JniCallback& operator=(JniCallback const &) = delete;
JniCallback& operator=(JniCallback&&) = delete;
// create a JniCallback
static JniCallback* make(JNIEnv* env, jobject handler, jobject runnable);
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(JniCallback* callback);
// CallbackHandler interface.
void post(void* user, Callback callback) override;
// Get the CallbackHandler interface
filament::backend::CallbackHandler* getHandler() noexcept { return this; }
jobject getCallbackObject() { return mCallback; }
protected:
JniCallback(JNIEnv* env, jobject handler, jobject runnable);
explicit JniCallback() = default; // this version does nothing
virtual ~JniCallback();
jobject mHandler{};
jobject mCallback{};
CallbackJni mCallbackUtils{};
};
struct JniBufferCallback : public JniCallback {
// create a JniBufferCallback
static JniBufferCallback* make(filament::Engine* engine,
JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer);
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(void*, size_t, void* user);
private:
JniBufferCallback(JNIEnv* env, jobject handler, jobject callback, AutoBuffer&& buffer);
virtual ~JniBufferCallback();
AutoBuffer mBuffer;
};
struct JniImageCallback : public JniCallback {
// create a JniImageCallback
static JniImageCallback* make(filament::Engine* engine, JNIEnv* env, jobject handler,
jobject runnable, long image);
// execute the callback on the java thread and destroy ourselves
static void postToJavaAndDestroy(void*, void* user);
private:
JniImageCallback(JNIEnv* env, jobject handler, jobject runnable, long image);
virtual ~JniImageCallback();
long mImage;
};

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "common/NioUtils.h"
#include <algorithm>
#include <utils/Log.h>
AutoBuffer::AutoBuffer(JNIEnv *env, jobject buffer, jint size, bool commit) noexcept :
mEnv(env),
mDoCommit(commit) {
mNioUtils.jniClass = env->FindClass("com/google/android/filament/NioUtils");
mNioUtils.jniClass = (jclass) env->NewGlobalRef(mNioUtils.jniClass);
mNioUtils.getBasePointer = env->GetStaticMethodID(mNioUtils.jniClass,
"getBasePointer", "(Ljava/nio/Buffer;JI)J");
mNioUtils.getBaseArray = env->GetStaticMethodID(mNioUtils.jniClass,
"getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;");
mNioUtils.getBaseArrayOffset = env->GetStaticMethodID(mNioUtils.jniClass,
"getBaseArrayOffset", "(Ljava/nio/Buffer;I)I");
mNioUtils.getBufferType = env->GetStaticMethodID(mNioUtils.jniClass,
"getBufferType", "(Ljava/nio/Buffer;)I");
mBuffer = env->NewGlobalRef(buffer);
mType = (BufferType) env->CallStaticIntMethod(
mNioUtils.jniClass, mNioUtils.getBufferType, mBuffer);
switch (mType) {
case BufferType::BYTE:
mShift = 0;
break;
case BufferType::CHAR:
case BufferType::SHORT:
mShift = 1;
break;
case BufferType::INT:
case BufferType::FLOAT:
mShift = 2;
break;
case BufferType::LONG:
case BufferType::DOUBLE:
mShift = 3;
break;
}
mSize = (size_t) size << mShift;
jlong address = (jlong) env->GetDirectBufferAddress(mBuffer);
if (address) {
// Direct buffer case
mData = reinterpret_cast<void *>(env->CallStaticLongMethod(mNioUtils.jniClass,
mNioUtils.getBasePointer, mBuffer, address, mShift));
mUserData = mData;
} else {
// wrapped array case
jarray array = (jarray) env->CallStaticObjectMethod(mNioUtils.jniClass,
mNioUtils.getBaseArray, mBuffer);
jint offset = env->CallStaticIntMethod(mNioUtils.jniClass,
mNioUtils.getBaseArrayOffset, mBuffer, mShift);
mBaseArray = (jarray) env->NewGlobalRef(array);
switch (mType) {
case BufferType::BYTE:
mData = env->GetByteArrayElements((jbyteArray)mBaseArray, nullptr);
break;
case BufferType::CHAR:
mData = env->GetCharArrayElements((jcharArray)mBaseArray, nullptr);
break;
case BufferType::SHORT:
mData = env->GetShortArrayElements((jshortArray)mBaseArray, nullptr);
break;
case BufferType::INT:
mData = env->GetIntArrayElements((jintArray)mBaseArray, nullptr);
break;
case BufferType::LONG:
mData = env->GetLongArrayElements((jlongArray)mBaseArray, nullptr);
break;
case BufferType::FLOAT:
mData = env->GetFloatArrayElements((jfloatArray)mBaseArray, nullptr);
break;
case BufferType::DOUBLE:
mData = env->GetDoubleArrayElements((jdoubleArray)mBaseArray, nullptr);
break;
}
mUserData = (void *) ((char *) mData + offset);
}
}
AutoBuffer::AutoBuffer(AutoBuffer &&rhs) noexcept {
mEnv = rhs.mEnv;
std::swap(mData, rhs.mData);
std::swap(mUserData, rhs.mUserData);
std::swap(mSize, rhs.mSize);
std::swap(mType, rhs.mType);
std::swap(mShift, rhs.mShift);
std::swap(mBuffer, rhs.mBuffer);
std::swap(mBaseArray, rhs.mBaseArray);
std::swap(mNioUtils, rhs.mNioUtils);
}
AutoBuffer::~AutoBuffer() noexcept {
JNIEnv *env = mEnv;
if (mBaseArray) {
jint mode = mDoCommit ? 0 : JNI_ABORT;
switch (mType) {
case BufferType::BYTE:
env->ReleaseByteArrayElements((jbyteArray)mBaseArray, (jbyte *) mData, mode);
break;
case BufferType::CHAR:
env->ReleaseCharArrayElements((jcharArray)mBaseArray, (jchar *) mData, mode);
break;
case BufferType::SHORT:
env->ReleaseShortArrayElements((jshortArray)mBaseArray, (jshort *) mData, mode);
break;
case BufferType::INT:
env->ReleaseIntArrayElements((jintArray)mBaseArray, (jint *) mData, mode);
break;
case BufferType::LONG:
env->ReleaseLongArrayElements((jlongArray)mBaseArray, (jlong *) mData, mode);
break;
case BufferType::FLOAT:
env->ReleaseFloatArrayElements((jfloatArray)mBaseArray, (jfloat *) mData, mode);
break;
case BufferType::DOUBLE:
env->ReleaseDoubleArrayElements((jdoubleArray)mBaseArray, (jdouble *) mData, mode);
break;
}
env->DeleteGlobalRef(mBaseArray);
}
if (mBuffer) {
env->DeleteGlobalRef(mBuffer);
}
mEnv->DeleteGlobalRef(mNioUtils.jniClass);
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstdint>
#include <jni.h>
class AutoBuffer {
public:
enum class BufferType : uint8_t {
BYTE,
CHAR,
SHORT,
INT,
LONG,
FLOAT,
DOUBLE
};
// Clients should pass "true" for the commit argument if they intend to mutate the buffer
// contents from native code.
AutoBuffer(JNIEnv* env, jobject buffer, jint size, bool commit = false) noexcept;
AutoBuffer(AutoBuffer&& rhs) noexcept;
~AutoBuffer() noexcept;
void attachToJniThread(JNIEnv* env) noexcept {
mEnv = env;
}
void* getData() const noexcept {
return mUserData;
}
size_t getSize() const noexcept {
return mSize;
}
size_t getShift() const noexcept {
return mShift;
}
size_t countToByte(size_t count) const noexcept {
return count << mShift;
}
private:
void* mUserData = nullptr;
size_t mSize = 0;
BufferType mType = BufferType::BYTE;
uint8_t mShift = 0;
JNIEnv* mEnv;
void* mData = nullptr;
jobject mBuffer = nullptr;
jarray mBaseArray = nullptr;
bool mDoCommit = false;
struct {
jclass jniClass;
jmethodID getBasePointer;
jmethodID getBaseArray;
jmethodID getBaseArrayOffset;
jmethodID getBufferType;
} mNioUtils{};
};

View File

@@ -1,96 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_ENUMMANAGER_H
#define TNT_ENUMMANAGER_H
#include <algorithm>
#include <string>
#include <unordered_map>
#include <filamat/MaterialBuilder.h>
namespace filamat {
using Property = MaterialBuilder::Property;
using UniformType = MaterialBuilder::UniformType;
using SamplerType = MaterialBuilder::SamplerType;
using SubpassType = MaterialBuilder::SubpassType;
using SamplerFormat = MaterialBuilder::SamplerFormat;
using ParameterPrecision = MaterialBuilder::ParameterPrecision;
using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
// Convenience methods to convert std::string to Enum and also iterate over Enum values.
class Enums {
public:
// Returns true if string "s" is a valid string representation of an element of enum T.
template<typename T>
static bool isValid(const std::string& s) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map.find(s) != map.end();
}
// Return enum matching its string representation. Returns undefined if s is not a valid enum T
// value. You should always call isValid() first to validate a string before calling toEnum().
template<typename T>
static T toEnum(const std::string& s) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map.at(s);
}
template<typename T>
static std::string toString(T t) noexcept;
// Return a map of all values in an enum with their string representation.
template<typename T>
static std::unordered_map<std::string, T>& map() noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
return map;
};
private:
template<typename T>
static std::unordered_map<std::string, T>& getMap() noexcept;
static std::unordered_map<std::string, Property> mStringToProperty;
static std::unordered_map<std::string, UniformType> mStringToUniformType;
static std::unordered_map<std::string, SamplerType> mStringToSamplerType;
static std::unordered_map<std::string, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
};
template<typename T>
std::string Enums::toString(T t) noexcept {
std::unordered_map<std::string, T>& map = getMap<T>();
auto result = std::find_if(map.begin(), map.end(), [t](auto& pair) {
return pair.second == t;
});
if (result != map.end()) {
return result->first;
}
return "";
}
} // namespace filamat
#endif //TNT_ENUMMANAGER_H

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_INCLUDER_H
#define TNT_FILAMAT_INCLUDER_H
#include <utils/CString.h>
#include <functional>
namespace filamat {
struct IncludeResult {
// The include name of the root file, as if it were being included.
// I.e., 'foobar.h' in the case of #include "foobar.h"
const utils::CString includeName;
// The following fields should be filled out by the IncludeCallback when processing an include,
// or when calling resolveIncludes for the root file.
// The full contents of the include file. This may contain additional, recursive include
// directives.
utils::CString text;
// The line number for the first line of text (first line is 0).
size_t lineNumberOffset = 0;
// The name of the include file. This gets passed as "includerName" for any includes inside of
// source. This field isn't used by the include system; it's up to the callback to give meaning
// to this value and interpret it accordingly. In the case of DirIncluder, this is an empty
// string to represent the root include file, and a canonical path for subsequent included
// files.
utils::CString name;
};
/**
* A callback invoked by the include system when an #include "file.h" directive is found.
*
* For example, if a file main.h includes file.h on line 10, then IncludeCallback would be called
* with the following:
* includeCallback("main.h", {.includeName = "file.h" })
* It's then up to the IncludeCallback to fill out the .text, .name, and (optionally)
* lineNumberOffset fields.
*
* @param includedBy is the value that was given to IncludeResult.name for this source file, or
* the empty string for the root source file.
* @param result is the IncludeResult that the callback should fill out.
* @return true, if the include was resolved successfully, false otherwise.
*
* For an example of implementing this callback, see tools/matc/src/matc/DirIncluder.h.
*/
using IncludeCallback = std::function<bool(
const utils::CString& includedBy,
IncludeResult& result)>;
} // namespace filamat
#endif

View File

@@ -1,746 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H
#define TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H
#include <cstddef>
#include <cstdint>
#include <atomic>
#include <string>
#include <vector>
#include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h>
#include <filament/MaterialEnums.h>
#include <filamat/IncludeCallback.h>
#include <filamat/Package.h>
#include <utils/BitmaskEnum.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
namespace utils {
class JobSystem;
}
namespace filamat {
struct MaterialInfo;
class ChunkContainer;
struct Variant;
class UTILS_PUBLIC MaterialBuilderBase {
public:
/**
* High-level hint that works in concert with TargetApi to determine the shader models (used to
* generate GLSL) and final output representations (spirv and/or text).
*/
enum class Platform {
DESKTOP,
MOBILE,
ALL
};
enum class TargetApi : uint8_t {
OPENGL = 0x01u,
VULKAN = 0x02u,
METAL = 0x04u,
ALL = OPENGL | VULKAN | METAL
};
enum class TargetLanguage {
GLSL, // GLSL with OpenGL semantics
SPIRV // GLSL with Vulkan semantics
};
enum class Optimization {
NONE,
PREPROCESSOR,
SIZE,
PERFORMANCE
};
/**
* Initialize MaterialBuilder.
*
* init must be called first before building any materials.
*/
static void init();
/**
* Release internal MaterialBuilder resources.
*
* Call shutdown when finished building materials to release all internal resources. After
* calling shutdown, another call to MaterialBuilder::init must precede another material build.
*/
static void shutdown();
protected:
// Looks at platform and target API, then decides on shader models and output formats.
void prepare(bool vulkanSemantics);
using ShaderModel = filament::backend::ShaderModel;
Platform mPlatform = Platform::DESKTOP;
TargetApi mTargetApi = (TargetApi) 0;
Optimization mOptimization = Optimization::PERFORMANCE;
bool mPrintShaders = false;
bool mGenerateDebugInfo = false;
utils::bitset32 mShaderModels;
struct CodeGenParams {
int shaderModel;
TargetApi targetApi;
TargetLanguage targetLanguage;
};
std::vector<CodeGenParams> mCodeGenPermutations;
// For finding properties and running semantic analysis, we always use the same code gen
// permutation. This is the first permutation generated with default arguments passed to matc.
const CodeGenParams mSemanticCodeGenParams = {
.shaderModel = (int) ShaderModel::GL_ES_30,
.targetApi = TargetApi::OPENGL,
.targetLanguage = TargetLanguage::SPIRV
};
uint8_t mVariantFilter = 0;
// Keeps track of how many times MaterialBuilder::init() has been called without a call to
// MaterialBuilder::shutdown(). Internally, glslang does something similar. We keep track for
// ourselves so we can inform the user if MaterialBuilder::init() hasn't been called before
// attempting to build a material.
static std::atomic<int> materialBuilderClients;
};
// Utility function that looks at an Engine backend to determine TargetApi
inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend(
filament::backend::Backend backend) noexcept {
using filament::backend::Backend;
using TargetApi = MaterialBuilderBase::TargetApi;
switch (backend) {
case Backend::DEFAULT: return TargetApi::ALL;
case Backend::OPENGL: return TargetApi::OPENGL;
case Backend::VULKAN: return TargetApi::VULKAN;
case Backend::METAL: return TargetApi::METAL;
case Backend::NOOP: return TargetApi::OPENGL;
}
}
/**
* MaterialBuilder builds Filament materials from shader code.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filamat/MaterialBuilder.h>
* using namespace filamat;
*
* // Must be called before any materials can be built.
* MaterialBuilder::init();
* MaterialBuilder builder;
* builder
* .name("My material")
* .material("void material (inout MaterialInputs material) {"
* " prepareMaterial(material);"
* " material.baseColor.rgb = float3(1.0, 0.0, 0.0);"
* "}")
* .shading(MaterialBuilder::Shading::LIT)
* .targetApi(MaterialBuilder::TargetApi::ALL)
* .platform(MaterialBuilder::Platform::ALL);
* Package package = builder.build();
* if (package.isValid()) {
* // success!
* }
* // Call when finished building all materials to release internal
* // MaterialBuilder resources.
* MaterialBuilder::shutdown();
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @see filament::Material
*/
class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase {
public:
MaterialBuilder();
static constexpr size_t MATERIAL_VARIABLES_COUNT = 4;
enum class Variable : uint8_t {
CUSTOM0,
CUSTOM1,
CUSTOM2,
CUSTOM3
// when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT
};
using MaterialDomain = filament::MaterialDomain;
using RefractionMode = filament::RefractionMode;
using RefractionType = filament::RefractionType;
using ShaderQuality = filament::ShaderQuality;
using BlendingMode = filament::BlendingMode;
using Shading = filament::Shading;
using Interpolation = filament::Interpolation;
using VertexDomain = filament::VertexDomain;
using TransparencyMode = filament::TransparencyMode;
using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion;
using UniformType = filament::backend::UniformType;
using SamplerType = filament::backend::SamplerType;
using SubpassType = filament::backend::SubpassType;
using SamplerFormat = filament::backend::SamplerFormat;
using ParameterPrecision = filament::backend::Precision;
using CullingMode = filament::backend::CullingMode;
enum class VariableQualifier : uint8_t {
OUT
};
enum class OutputTarget : uint8_t {
COLOR,
DEPTH
};
enum class OutputType : uint8_t {
FLOAT,
FLOAT2,
FLOAT3,
FLOAT4
};
struct PreprocessorDefine {
std::string name;
std::string value;
PreprocessorDefine(const std::string& name, const std::string& value) :
name(name), value(value) {}
};
using PreprocessorDefineList = std::vector<PreprocessorDefine>;
//! Set the name of this material.
MaterialBuilder& name(const char* name) noexcept;
//! Set the file name of this material file. Used in error reporting.
MaterialBuilder& fileName(const char* name) noexcept;
//! Set the shading model.
MaterialBuilder& shading(Shading shading) noexcept;
//! Set the interpolation mode.
MaterialBuilder& interpolation(Interpolation interpolation) noexcept;
//! Add a parameter (i.e., a uniform) to this material.
MaterialBuilder& parameter(UniformType type, ParameterPrecision precision,
const char* name) noexcept;
//! Add a parameter (i.e., a uniform) to this material.
MaterialBuilder& parameter(UniformType type, const char* name) noexcept {
return parameter(type, ParameterPrecision::DEFAULT, name);
}
//! Add a parameter array to this material.
MaterialBuilder& parameter(UniformType type, size_t size,
ParameterPrecision precision, const char* name) noexcept;
//! Add a parameter array to this material.
MaterialBuilder& parameter(UniformType type, size_t size, const char* name) noexcept {
return parameter(type, size, ParameterPrecision::DEFAULT, name);
}
/**
* Add a sampler parameter to this material.
*
* When SamplerType::SAMPLER_EXTERNAL is specifed, format and precision are ignored.
*/
MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format,
ParameterPrecision precision, const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format,
const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, ParameterPrecision precision,
const char* name) noexcept;
/// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*)
MaterialBuilder& parameter(SamplerType samplerType, const char* name) noexcept;
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
/**
* Require a specified attribute.
*
* position is always required and normal depends on the shading model.
*/
MaterialBuilder& require(filament::VertexAttribute attribute) noexcept;
//! Specify the domain that this material will operate in.
MaterialBuilder& materialDomain(MaterialDomain materialDomain) noexcept;
/**
* Set the code content of this material.
*
* Surface Domain
* --------------
*
* Materials in the SURFACE domain must declare a function:
* ~~~~~
* void material(inout MaterialInputs material) {
* prepareMaterial(material);
* material.baseColor.rgb = float3(1.0, 0.0, 0.0);
* }
* ~~~~~
* this function *must* call `prepareMaterial(material)` before it returns.
*
* Post-process Domain
* -------------------
*
* Materials in the POST_PROCESS domain must declare a function:
* ~~~~~
* void postProcess(inout PostProcessInputs postProcess) {
* postProcess.color = float4(1.0);
* }
* ~~~~~
*
* @param code The source code of the material.
* @param line The line number offset of the material, where 0 is the first line. Used for error
* reporting
*/
MaterialBuilder& material(const char* code, size_t line = 0) noexcept;
/**
* Set the callback used for resolving include directives.
* The default is no callback, which disallows all includes.
*/
MaterialBuilder& includeCallback(IncludeCallback callback) noexcept;
/**
* Set the vertex code content of this material.
*
* Surface Domain
* --------------
*
* Materials in the SURFACE domain must declare a function:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void materialVertex(inout MaterialVertexInputs material) {
*
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Post-process Domain
* -------------------
*
* Materials in the POST_PROCESS domain must declare a function:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void postProcessVertex(inout PostProcessVertexInputs postProcess) {
*
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* @param code The source code of the material.
* @param line The line number offset of the material, where 0 is the first line. Used for error
* reporting
*/
MaterialBuilder& materialVertex(const char* code, size_t line = 0) noexcept;
MaterialBuilder& quality(ShaderQuality quality) noexcept;
//! Set the blending mode for this material.
MaterialBuilder& blending(BlendingMode blending) noexcept;
/**
* Set the blending mode of the post-lighting color for this material.
* Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT.
* This setting requires the material property "postLightingColor" to be set.
*/
MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept;
//! Set the vertex domain for this material.
MaterialBuilder& vertexDomain(VertexDomain domain) noexcept;
/**
* How triangles are culled by default (doesn't affect points or lines, BACK by default).
* Material instances can override this.
*/
MaterialBuilder& culling(CullingMode culling) noexcept;
//! Enable / disable color-buffer write (enabled by default, material instances can override).
MaterialBuilder& colorWrite(bool enable) noexcept;
//! Enable / disable depth-buffer write (enabled by default for opaque, disabled for others, material instances can override).
MaterialBuilder& depthWrite(bool enable) noexcept;
//! Enable / disable depth based culling (enabled by default, material instances can override).
MaterialBuilder& depthCulling(bool enable) noexcept;
/**
* Double-sided materials don't cull faces, equivalent to culling(CullingMode::NONE).
* doubleSided() overrides culling() if called.
* When called with "false", this enables the capability for a run-time toggle.
*/
MaterialBuilder& doubleSided(bool doubleSided) noexcept;
/**
* Any fragment with an alpha below this threshold is clipped (MASKED blending mode only).
* The mask threshold can also be controlled by using the float material parameter called
* `_maskThreshold`, or by calling
* @ref filament::MaterialInstance::setMaskThreshold "MaterialInstance::setMaskThreshold".
*/
MaterialBuilder& maskThreshold(float threshold) noexcept;
//! The material output is multiplied by the shadowing factor (UNLIT model only).
MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept;
//! This material casts transparent shadows. The blending mode must be TRANSPARENT or FADE.
MaterialBuilder& transparentShadow(bool transparentShadow) noexcept;
/**
* Reduces specular aliasing for materials that have low roughness. Turning this feature on also
* helps preserve the shapes of specular highlights as an object moves away from the camera.
* When turned on, two float material parameters are added to control the effect:
* `_specularAAScreenSpaceVariance` and `_specularAAThreshold`. You can also use
* @ref filament::MaterialInstance::setSpecularAntiAliasingVariance
* "MaterialInstance::setSpecularAntiAliasingVariance" and
* @ref filament::MaterialInstance::setSpecularAntiAliasingThreshold
* "setSpecularAntiAliasingThreshold"
*
* Disabled by default.
*/
MaterialBuilder& specularAntiAliasing(bool specularAntiAliasing) noexcept;
/**
* Sets the screen-space variance of the filter kernel used when applying specular
* anti-aliasing. The default value is set to 0.15. The specified value should be between 0 and
* 1 and will be clamped if necessary.
*/
MaterialBuilder& specularAntiAliasingVariance(float screenSpaceVariance) noexcept;
/**
* Sets the clamping threshold used to suppress estimation errors when applying specular
* anti-aliasing. The default value is set to 0.2. The specified value should be between 0 and 1
* and will be clamped if necessary.
*/
MaterialBuilder& specularAntiAliasingThreshold(float threshold) noexcept;
/**
* Enables or disables the index of refraction (IoR) change caused by the clear coat layer when
* present. When the IoR changes, the base color is darkened. Disabling this feature preserves
* the base color as initially specified.
*
* Enabled by default.
*/
MaterialBuilder& clearCoatIorChange(bool clearCoatIorChange) noexcept;
//! Enable / disable flipping of the Y coordinate of UV attributes, enabled by default.
MaterialBuilder& flipUV(bool flipUV) noexcept;
//! Enable / disable multi-bounce ambient occlusion, disabled by default on mobile.
MaterialBuilder& multiBounceAmbientOcclusion(bool multiBounceAO) noexcept;
//! Set the specular ambient occlusion technique. Disabled by default on mobile.
MaterialBuilder& specularAmbientOcclusion(SpecularAmbientOcclusion specularAO) noexcept;
//! Specify the refraction
MaterialBuilder& refractionMode(RefractionMode refraction) noexcept;
//! Specify the refraction type
MaterialBuilder& refractionType(RefractionType refractionType) noexcept;
//! Specifies how transparent objects should be rendered (default is DEFAULT).
MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept;
/**
* Enable / disable custom surface shading. Custom surface shading requires the LIT
* shading model. In addition, the following function must be defined in the fragment
* block:
*
* ~~~~~
* vec3 surfaceShading(const MaterialInputs materialInputs,
* const ShadingData shadingData, const LightData lightData) {
*
* return vec3(1.0); // Compute surface shading with custom BRDF, etc.
* }
* ~~~~~
*
* This function is invoked once per light. Please refer to the materials documentation
* for more information about the different parameters.
*
* @param customSurfaceShading Enables or disables custom surface shading
*/
MaterialBuilder& customSurfaceShading(bool customSurfaceShading) noexcept;
/**
* Specifies desktop vs mobile; works in concert with TargetApi to determine the shader models
* (used to generate code) and final output representations (spirv and/or text).
*/
MaterialBuilder& platform(Platform platform) noexcept;
/**
* Specifies OpenGL, Vulkan, or Metal.
* This can be called repeatedly to build for multiple APIs.
* Works in concert with Platform to determine the shader models (used to generate code) and
* final output representations (spirv and/or text).
* If linking against filamat_lite, only `OPENGL` is allowed.
*/
MaterialBuilder& targetApi(TargetApi targetApi) noexcept;
/**
* Specifies the level of optimization to apply to the shaders (default is PERFORMANCE).
* If linking against filamat_lite, this _must_ be called with Optimization::NONE.
*/
MaterialBuilder& optimization(Optimization optimization) noexcept;
// TODO: this is present here for matc's "--print" flag, but ideally does not belong inside
// MaterialBuilder.
//! If true, will output the generated GLSL shader code to stdout.
MaterialBuilder& printShaders(bool printShaders) noexcept;
//! If true, will include debugging information in generated SPIRV.
MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept;
//! Specifies a list of variants that should be filtered out during code generation.
MaterialBuilder& variantFilter(uint8_t variantFilter) noexcept;
//! Adds a new preprocessor macro definition to the shader code. Can be called repeatedly.
MaterialBuilder& shaderDefine(const char* name, const char* value) noexcept;
//! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain.
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target,
OutputType type, const char* name, int location = -1) noexcept;
MaterialBuilder& enableFramebufferFetch() noexcept;
/**
* Build the material. If you are using the Filament engine with this library, you should use
* the job system provided by Engine.
*/
Package build(utils::JobSystem& jobSystem) noexcept;
public:
// The methods and types below are for internal use
/// @cond never
/**
* Add a subpass parameter to this material.
*/
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, ParameterPrecision
precision, const char* name) noexcept;
MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, const char* name)
noexcept;
MaterialBuilder& parameter(SubpassType subpassType, ParameterPrecision precision,
const char* name) noexcept;
MaterialBuilder& parameter(SubpassType subpassType, const char* name) noexcept;
struct Parameter {
Parameter() noexcept : parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
: name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { }
// Subpass
Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p)
: name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { }
utils::CString name;
size_t size;
UniformType uniformType;
ParameterPrecision precision;
SamplerType samplerType;
SubpassType subpassType;
SamplerFormat format;
enum {
INVALID,
UNIFORM,
SAMPLER,
SUBPASS
} parameterType;
bool isSampler() const { return parameterType == SAMPLER; }
bool isUniform() const { return parameterType == UNIFORM; }
bool isSubpass() const { return parameterType == SUBPASS; }
};
struct Output {
Output() noexcept = default;
Output(const char* outputName, VariableQualifier qualifier, OutputTarget target,
OutputType type, int location) noexcept
: name(outputName), qualifier(qualifier), target(target), type(type),
location(location) { }
utils::CString name;
VariableQualifier qualifier;
OutputTarget target;
OutputType type;
int location;
};
static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT;
using Property = filament::Property;
using PropertyList = bool[MATERIAL_PROPERTIES_COUNT];
using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT];
using OutputList = std::vector<Output>;
static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
static constexpr size_t MAX_DEPTH_OUTPUT = 1;
static_assert(MAX_COLOR_OUTPUT == 8,
"When updating MRT::TARGET_COUNT, manually update post_process_inputs.fs"
" and post_process.fs");
// Preview the first shader generated by the given CodeGenParams.
// This is used to run Static Code Analysis before generating a package.
const std::string peek(filament::backend::ShaderType type,
const CodeGenParams& params, const PropertyList& properties) noexcept;
// Returns true if any of the parameter samplers is of type samplerExternal
bool hasExternalSampler() const noexcept;
static constexpr size_t MAX_PARAMETERS_COUNT = 48;
static constexpr size_t MAX_SUBPASS_COUNT = 1;
using ParameterList = Parameter[MAX_PARAMETERS_COUNT];
// returns the number of parameters declared in this material
uint8_t getParameterCount() const noexcept { return mParameterCount; }
// returns a list of at least getParameterCount() parameters
const ParameterList& getParameters() const noexcept { return mParameters; }
uint8_t getVariantFilter() const { return mVariantFilter; }
/// @endcond
private:
void prepareToBuild(MaterialInfo& info) noexcept;
// Return true if the shader is syntactically and semantically valid.
// This method finds all the properties defined in the fragment and
// vertex shaders of the material.
bool findAllProperties() noexcept;
// Multiple calls to findProperties accumulate the property sets across fragment
// and vertex shaders in mProperties.
bool findProperties(filament::backend::ShaderType type,
MaterialBuilder::PropertyList& p) noexcept;
bool runSemanticAnalysis() noexcept;
bool checkLiteRequirements() noexcept;
void writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept;
void writeSurfaceChunks(ChunkContainer& container) const noexcept;
bool generateShaders(
utils::JobSystem& jobSystem,
const std::vector<Variant>& variants, ChunkContainer& container,
const MaterialInfo& info) const noexcept;
bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; }
utils::CString mMaterialName;
utils::CString mFileName;
class ShaderCode {
public:
void setLineOffset(size_t offset) noexcept { mLineOffset = offset; }
void setUnresolved(const utils::CString& code) noexcept {
mIncludesResolved = false;
mCode = code;
}
// Resolve all the #include directives, returns true if successful.
bool resolveIncludes(IncludeCallback callback, const utils::CString& fileName) noexcept;
const utils::CString& getResolved() const noexcept {
assert(mIncludesResolved);
return mCode;
}
size_t getLineOffset() const noexcept { return mLineOffset; }
private:
utils::CString mCode;
size_t mLineOffset = 0;
bool mIncludesResolved = false;
};
ShaderCode mMaterialCode;
ShaderCode mMaterialVertexCode;
IncludeCallback mIncludeCallback = nullptr;
PropertyList mProperties;
ParameterList mParameters;
VariableList mVariables;
OutputList mOutputs;
ShaderQuality mShaderQuality = ShaderQuality::DEFAULT;
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT;
CullingMode mCullingMode = CullingMode::BACK;
Shading mShading = Shading::LIT;
MaterialDomain mMaterialDomain = MaterialDomain::SURFACE;
RefractionMode mRefractionMode = RefractionMode::NONE;
RefractionType mRefractionType = RefractionType::SOLID;
Interpolation mInterpolation = Interpolation::SMOOTH;
VertexDomain mVertexDomain = VertexDomain::OBJECT;
TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT;
filament::AttributeBitset mRequiredAttributes;
float mMaskThreshold = 0.4f;
float mSpecularAntiAliasingVariance = 0.15f;
float mSpecularAntiAliasingThreshold = 0.2f;
bool mShadowMultiplier = false;
bool mTransparentShadow = false;
uint8_t mParameterCount = 0;
bool mDoubleSided = false;
bool mDoubleSidedCapability = false;
bool mColorWrite = true;
bool mDepthTest = true;
bool mDepthWrite = true;
bool mDepthWriteSet = false;
bool mSpecularAntiAliasing = false;
bool mClearCoatIorChange = true;
bool mFlipUV = true;
bool mMultiBounceAO = false;
bool mMultiBounceAOSet = false;
SpecularAmbientOcclusion mSpecularAO = SpecularAmbientOcclusion::NONE;
bool mSpecularAOSet = false;
bool mCustomSurfaceShading = false;
bool mEnableFramebufferFetch = false;
PreprocessorDefineList mDefines;
};
} // namespace filamat
template<> struct utils::EnableBitMaskOperators<filamat::MaterialBuilder::TargetApi>
: public std::true_type {};
#endif

View File

@@ -1,103 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_PACKAGE_H
#define TNT_FILAMAT_PACKAGE_H
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <cstddef>
#include <functional>
#include <utils/compiler.h>
namespace filamat {
class UTILS_PUBLIC Package {
public:
Package() = default;
// Regular constructor
explicit Package(size_t size) : mSize(size) {
mPayload = new uint8_t[size];
}
Package(const void* src, size_t size) : Package(size) {
memcpy(mPayload, src, size);
}
// Move Constructor
Package(Package&& other) noexcept : mPayload(other.mPayload), mSize(other.mSize),
mValid(other.mValid) {
other.mPayload = nullptr;
other.mSize = 0;
other.mValid = false;
}
// Move assignment
Package& operator=(Package&& other) noexcept {
std::swap(mPayload, other.mPayload);
std::swap(mSize, other.mSize);
std::swap(mValid, other.mValid);
return *this;
}
// Copy assignment operator disallowed.
Package& operator=(const Package& other) = delete;
// Copy constructor disallowed.
Package(const Package& other) = delete;
~Package() {
delete[] mPayload;
}
uint8_t* getData() const noexcept {
return mPayload;
}
size_t getSize() const noexcept {
return mSize;
}
uint8_t* getEnd() const noexcept {
return mPayload + mSize;
}
void setValid(bool valid) noexcept {
mValid = valid;
}
bool isValid() const noexcept {
return mValid;
}
static Package invalidPackage() {
Package package(0);
package.setValid(false);
return package;
}
private:
uint8_t* mPayload = nullptr;
size_t mSize = 0;
bool mValid = true;
};
} // namespace filamat
#endif

View File

@@ -1,242 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_IBL_PREFILTER_IBLPREFILTER_H
#define TNT_IBL_PREFILTER_IBLPREFILTER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <filament/Texture.h>
namespace filament {
class Engine;
class View;
class Scene;
class Renderer;
class Material;
class MaterialInstance;
class VertexBuffer;
class IndexBuffer;
class Camera;
class Texture;
} // namespace filament
/**
* IBLPrefilterContext creates and initializes GPU state common to all environment map filters
* supported. Typically, only one instance per filament Engine of this object needs to exist.
*
* Usage Example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
*
* IBLPrefilterContext context(engine);
* IBLPrefilterContext::SpecularFilter filter(context);
* Texture* texture = filter(environment_cubemap);
*
* IndirectLight* indirectLight = IndirectLight::Builder()
* .reflections(texture)
* .build(engine);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC IBLPrefilterContext {
public:
/**
* Creates an IBLPrefilter context.
* @param engine filament engine to use
*/
IBLPrefilterContext(filament::Engine& engine);
/**
* Destroys all GPU resources created during initialization.
*/
~IBLPrefilterContext() noexcept;
// not copyable
IBLPrefilterContext(IBLPrefilterContext const&) = delete;
IBLPrefilterContext& operator=(IBLPrefilterContext const&) = delete;
// movable
IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept;
IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs);
// -------------------------------------------------------------------------------------------
/**
* EquirectangularToCubemap is use to convert an equirectangluar image to a cubemap.
*/
class EquirectangularToCubemap {
public:
/**
* Creates a EquirectangularToCubemap processor.
* @param context IBLPrefilterContext to use
*/
explicit EquirectangularToCubemap(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~EquirectangularToCubemap() noexcept;
EquirectangularToCubemap(EquirectangularToCubemap const&) = delete;
EquirectangularToCubemap& operator=(EquirectangularToCubemap const&) = delete;
EquirectangularToCubemap(EquirectangularToCubemap&& rhs) noexcept;
EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs);
/**
* Converts an equirectangular image to a cubemap.
* @param equirectangular Texture to convert to a cubemap.
* - Can't be null.
* - Must be a 2d texture
* - Must have equirectangular geometry, that is width == 2*height.
* - Must be allocated with all mip levels.
* - Must be SAMPLEABLE
* @param outCubemap Output cubemap. If null the texture is automatically created
* with default parameters (size of 256 with 5 levels).
* - Must be a cubemap
* - Must have SAMPLEABLE and COLOR_ATTACHMENT usage bits
* @return returns outCubemap
*/
filament::Texture* operator()(
filament::Texture const* equirectangular,
filament::Texture* outCubemap = nullptr);
private:
IBLPrefilterContext& mContext;
filament::Material* mEquirectMaterial = nullptr;
};
/**
* SpecularFilter is a GPU based implementation of the specular probe pre-integration filter.
* An instance of SpecularFilter is needed per filter configuration. A filter configuration
* contains the filter's kernel and sample count.
*/
class SpecularFilter {
public:
enum class Kernel : uint8_t {
D_GGX, // Trowbridge-reitz distribution
};
/**
* Filter configuration.
*/
struct Config {
uint16_t sampleCount = 1024u; //!< filter sample count (max 2048)
uint8_t levelCount = 5u; //!< number of roughness levels
Kernel kernel = Kernel::D_GGX; //!< filter kernel
};
/**
* Filtering options for the current environment.
*/
struct Options {
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
float lodOffset = 1.0f; //!< Good values are 1.0 or 2.0. Higher values help with heavily HDR inputs.
bool generateMipmap = true; //!< set to false if the environment map already has mipmaps
};
/**
* Creates a SpecularFilter processor.
* @param context IBLPrefilterContext to use
* @param config Configuration of the filter
*/
SpecularFilter(IBLPrefilterContext& context, Config config);
/**
* Creates a filter with the default configuration.
* @param context IBLPrefilterContext to use
*/
explicit SpecularFilter(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~SpecularFilter() noexcept;
SpecularFilter(SpecularFilter const&) = delete;
SpecularFilter& operator=(SpecularFilter const&) = delete;
SpecularFilter(SpecularFilter&& rhs) noexcept;
SpecularFilter& operator=(SpecularFilter&& rhs);
/**
* Generates a prefiltered cubemap.
* @param options Options for this environment
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outReflectionsTexture Output prefiltered texture or, if null, it is
* automatically created with some default parameters.
* outReflectionsTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages and at
* least the same number of levels than requested by Config.
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(Options options,
filament::Texture const* environmentCubemap,
filament::Texture* outReflectionsTexture = nullptr);
/**
* Generates a prefiltered cubemap.
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. All mipmap levels will be overwritten.
* @param outReflectionsTexture Output prefiltered texture or, if null, it is
* automatically created with some default parameters.
* outReflectionsTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages and at
* least the same number of levels than requested by Config.
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(
filament::Texture const* environmentCubemap,
filament::Texture* outReflectionsTexture = nullptr);
// TODO: option for progressive filtering
// TODO: add a callback for when the processing is done?
private:
filament::Texture* createReflectionsTexture();
IBLPrefilterContext& mContext;
filament::Material* mKernelMaterial = nullptr;
filament::Texture* mKernelTexture = nullptr;
uint32_t mSampleCount = 0u;
uint8_t mLevelCount = 1u;
};
private:
friend class Filter;
filament::Engine& mEngine;
filament::Renderer* mRenderer{};
filament::Scene* mScene{};
filament::VertexBuffer* mVertexBuffer{};
filament::IndexBuffer* mIndexBuffer{};
filament::Camera* mCamera{};
utils::Entity mFullScreenQuadEntity{};
utils::Entity mCameraEntity{};
filament::View* mView{};
filament::Material* mIntegrationMaterial{};
};
#endif //TNT_IBL_PREFILTER_IBLPREFILTER_H

View File

@@ -54,18 +54,6 @@ public:
Type type; //!< property type
};
struct PropertyArray {
Property const* array;
size_t size;
};
/**
* Queries the list of all available properties.
*
* @return A pair containing a pointer to a Property array and the size of this array.
*/
PropertyArray getProperties() const noexcept;
/**
* Queries whether a property exists
* @param name The name of the property to query
@@ -123,6 +111,24 @@ public:
bool getProperty(const char* name, math::float4* v) const noexcept;
/** @}*/
struct DataSource {
void const* data;
size_t count;
};
DataSource getDataSource(const char* name) const noexcept;
struct FrameHistory {
using duration_ms = float;
duration_ms target{};
duration_ms targetWithHeadroom{};
duration_ms frameTime{};
duration_ms frameTimeDenoised{};
float scale = 1.0f;
float pid_e = 0.0f;
float pid_i = 0.0f;
float pid_d = 0.0f;
};
};

View File

@@ -35,9 +35,11 @@ class ColorGrading;
class DebugRegistry;
class Fence;
class IndexBuffer;
class SkinningBuffer;
class IndirectLight;
class Material;
class MaterialInstance;
class MorphTargetBuffer;
class Renderer;
class RenderTarget;
class Scene;
@@ -419,6 +421,8 @@ public:
bool destroy(const VertexBuffer* p); //!< Destroys an VertexBuffer object.
bool destroy(const Fence* p); //!< Destroys a Fence object.
bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object.
bool destroy(const SkinningBuffer* p); //!< Destroys a SkinningBuffer object.
bool destroy(const MorphTargetBuffer* p); //!< Destroys a MorphTargetBuffer object.
bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object.
/**

View File

@@ -273,6 +273,7 @@ public:
* Constant bias in depth-resolution units by which shadows are moved away from the
* light. The default value of 0.5 is used to round depth values up.
* Generally this value shouldn't be changed or at least be small and positive.
* This is ignored when the View's ShadowType is set to VSM.
*/
float polygonOffsetConstant = 0.5f;
@@ -281,6 +282,7 @@ public:
* away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW.
* Generally this value is between 0.5 and the size in texel of the PCF filter.
* Setting this value correctly is essential for LISPSM shadow-maps.
* This is ignored when the View's ShadowType is set to VSM.
*/
float polygonOffsetSlope = 2.0f;
@@ -332,6 +334,12 @@ public:
*/
float blurWidth = 0.0f;
} vsm;
/**
* Light bulb radius used for soft shadows. Currently this is only used when DPCF or PCSS is
* enabled. (2cm by default).
*/
float shadowBulbRadius = 0.02f;
};
struct ShadowCascades {
@@ -558,10 +566,11 @@ public:
* and are defined by the angle from the center axis to where the falloff begins (i.e.
* cones are defined by their half-angle).
*
* @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$
*
* @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$
* Both inner and outer are silently clamped to a minimum value of 0.5 degrees
* (~0.00873 radians) to avoid floating-point precision issues during rendering.
*
* @param inner inner cone angle in *radians* between 0.00873 and \p outer
* @param outer outer cone angle in *radians* between 0.00873 inner and @f$ \pi/2 @f$
* @return This Builder, for chaining calls.
*
* @note
@@ -812,8 +821,8 @@ public:
* Dynamically updates a spot light's cone as angles
*
* @param i Instance of the component obtained from getInstance().
* @param inner inner cone angle in *radians* between 0 and pi/2
* @param outer outer cone angle in *radians* between inner and pi/2
* @param inner inner cone angle in *radians* between 0.00873 and outer
* @param outer outer cone angle in *radians* between 0.00873 and pi/2
*
* @see Builder.spotLightCone()
*/

View File

@@ -193,9 +193,12 @@ public:
//! Returns the refraction mode used by this material.
RefractionMode getRefractionMode() const noexcept;
// Return the refraction type used by this material.
//! Return the refraction type used by this material.
RefractionType getRefractionType() const noexcept;
//! Returns the reflection mode used by this material.
ReflectionMode getReflectionMode() const noexcept;
/**
* Returns the number of parameters declared by this material.
* The returned value can be 0.

View File

@@ -62,6 +62,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialDomain = charTo64bitNum("MAT_DOMN"),
MaterialRefraction = charTo64bitNum("MAT_REFM"),
MaterialRefractionType = charTo64bitNum("MAT_REFT"),
MaterialReflectionMode = charTo64bitNum("MAT_REFL"),
MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"),
MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"),

View File

@@ -27,7 +27,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 12;
static constexpr size_t MATERIAL_VERSION = 17;
/**
* Supported shading models
@@ -136,20 +136,10 @@ enum VertexAttribute : uint8_t {
CUSTOM6 = 14,
CUSTOM7 = 15,
// Aliases for vertex morphing.
MORPH_POSITION_0 = CUSTOM0,
MORPH_POSITION_1 = CUSTOM1,
MORPH_POSITION_2 = CUSTOM2,
MORPH_POSITION_3 = CUSTOM3,
MORPH_TANGENTS_0 = CUSTOM4,
MORPH_TANGENTS_1 = CUSTOM5,
MORPH_TANGENTS_2 = CUSTOM6,
MORPH_TANGENTS_3 = CUSTOM7,
// this is limited by driver::MAX_VERTEX_ATTRIBUTE_COUNT
};
static constexpr size_t MAX_MORPH_TARGETS = 4;
static constexpr size_t MAX_MORPH_TARGETS = 128; // this is limited by filament::CONFIG_MAX_MORPH_TARGET_COUNT
static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8;
/**
@@ -186,6 +176,14 @@ enum class RefractionType : uint8_t {
THIN = 1, //!< refraction through thin objects (e.g. window)
};
/**
* Reflection mode
*/
enum class ReflectionMode : uint8_t {
DEFAULT = 0, //! reflections sample from the scene's IBL only
SCREEN_SPACE = 1, //! reflections sample from screen space, and fallback to the scene's IBL
};
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
using AttributeBitset = utils::bitset32;

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_MORPHTARGETBUFFER_H
#define TNT_FILAMENT_MORPHTARGETBUFFER_H
#include <filament/FilamentAPI.h>
#include <filament/Engine.h>
#include <math/mathfwd.h>
namespace filament {
/**
* MorphTargetBuffer is used to hold morphing data (positions and tangents).
*/
class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI {
struct BuilderDetails;
public:
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Size of the morph targets in vertex counts.
* @param vertexCount Number of vertex counts the morph targets can hold.
* @return A reference to this Builder for chaining calls.
*/
Builder& vertexCount(size_t vertexCount) noexcept;
/**
* Size of the morph targets in targets.
* @param count Number of targets the morph targets can hold.
* @return A reference to this Builder for chaining calls.
*/
Builder& count(size_t count) noexcept;
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this MorphTargetBuffer with.
*
* @return pointer to the newly created object or nullptr if exceptions are disabled and
* an error occurred.
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*/
MorphTargetBuffer* build(Engine& engine);
private:
friend class FMorphTargetBuffer;
};
/**
* Updates the position of morph target at the index.
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param weights pointer to at least count positions
* @param count number of position elements in positions
*/
void setPositionsAt(Engine& engine, size_t targetIndex, math::float3 const* positions, size_t count);
/**
* Updates the position of morph target at the index.
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param weights pointer to at least count positions
* @param count number of position elements in positions
*/
void setPositionsAt(Engine& engine, size_t targetIndex, math::float4 const* positions, size_t count);
/**
* Updates the position of morph target at the index.
* @param engine Reference to the filament::Engine associated with this MorphTargetBuffer.
* @param targetIndex the index of morph target to be updated.
* @param tangents pointer to at least count tangents
* @param count number of tangent elements in tangents
*/
void setTangentsAt(Engine& engine, size_t targetIndex, math::short4 const* tangents, size_t count);
/**
* Returns the vertex count of this MorphTargetBuffer.
* @return The number of vertices the MorphTargetBuffer holds.
*/
size_t getVertexCount() const noexcept;
/**
* Returns the target count of this MorphTargetBuffer.
* @return The number of targets the MorphTargetBuffer holds.
*/
size_t getCount() const noexcept;
};
} // namespace filament
#endif //TNT_FILAMENT_MORPHTARGETBUFFER_H

View File

@@ -284,6 +284,28 @@ struct AmbientOcclusionOptions {
} ssct;
};
/**
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
* @see setMultiSampleAntiAliasingOptions()
*/
struct MultiSampleAntiAliasingOptions {
bool enabled = false; //!< enables or disables msaa
/**
* sampleCount number of samples to use for multi-sampled anti-aliasing.\n
* 0: treated as 1
* 1: no anti-aliasing
* n: sample count. Effective sample could be different depending on the
* GPU capabilities.
*/
uint8_t sampleCount = 4;
/**
* custom resolve improves quality for HDR scenes, but may impact performance.
*/
bool customResolve = false;
};
/**
* Options for Temporal Anti-aliasing (TAA)
* @see setTemporalAntiAliasingOptions()
@@ -294,6 +316,18 @@ struct TemporalAntiAliasingOptions {
bool enabled = false; //!< enables or disables temporal anti-aliasing
};
/**
* Options for Screen-space Reflections.
* @see setScreenSpaceReflectionsOptions()
*/
struct ScreenSpaceReflectionsOptions {
float thickness = 0.5f; //!< ray thickness, in world units
float bias = 0.01f; //!< bias, in world units, to prevent self-intersections
float maxDistance = 3.0f; //!< maximum distance, in world units, to raycast
float stride = 1.0f; //!< stride, in texels, for samples along the ray.
bool enabled = false;
};
/**
* List of available post-processing anti-aliasing techniques.
* @see setAntiAliasing, getAntiAliasing, setSampleCount
@@ -317,7 +351,9 @@ enum class Dithering : uint8_t {
*/
enum class ShadowType : uint8_t {
PCF, //!< percentage-closer filtered shadows (default)
VSM //!< variance shadows
VSM, //!< variance shadows
DPCF, //!< PCF with contact hardening simulation
PCSS //!< PCF with soft shadows and contact hardening
};
/**
@@ -339,13 +375,6 @@ struct VsmShadowOptions {
*/
bool mipmapping = false;
/**
* EVSM exponent.
* The maximum value permissible is 5.54 for a shadow map in fp16, or 42.0 for a
* shadow map in fp32. Currently the shadow map bit depth is always fp16.
*/
float exponent = 5.54f;
/**
* VSM minimum variance scale, must be positive.
*/
@@ -357,6 +386,27 @@ struct VsmShadowOptions {
float lightBleedReduction = 0.15f;
};
/**
* View-level options for DPCF and PCSS Shadowing.
* @see setSoftShadowOptions()
* @warning This API is still experimental and subject to change.
*/
struct SoftShadowOptions {
/**
* Globally scales the penumbra of all DPCF and PCSS shadows
* Acceptable values are greater than 0
*/
float penumbraScale = 1.0f;
/**
* Globally scales the computed penumbra ratio of all DPCF and PCSS shadows.
* This effectively controls the strength of contact hardening effect and is useful for
* artistic purposes. Higher values make the shadows become softer faster.
* Acceptable values are equal to or greater than 1.
*/
float penumbraRatioScale = 1.0f;
};
} // namespace filament
#endif //TNT_FILAMENT_OPTIONS_H

View File

@@ -37,6 +37,12 @@ class Texture;
* An offscreen render target that can be associated with a View and contains
* weak references to a set of attached Texture objects.
*
* RenderTarget is intended to be used with the View's post-processing disabled for the most part.
* especially when a DEPTH attachment is also used (see Builder::texture()).
*
* Custom RenderTarget are ultimately intended to render into textures that might be used during
* the main render pass.
*
* Clients are responsible for the lifetime of all associated Texture attachments.
*
* @see View
@@ -87,6 +93,14 @@ public:
*
* All RenderTargets must have a non-null COLOR attachment.
*
* When using a DEPTH attachment, it is important to always disable post-processing
* in the View. Failing to do so will cause the DEPTH attachment to be ignored in most
* cases.
*
* When the intention is to keep the content of the DEPTH attachment after rendering,
* Usage::SAMPLEABLE must be set on the DEPTH attachment, otherwise the content of the
* DEPTH buffer may be discarded.
*
* @param attachment The attachment point of the texture.
* @param texture The associated texture object.
* @return A reference to this Builder for chaining calls.

View File

@@ -41,9 +41,11 @@ class Engine;
class IndexBuffer;
class Material;
class MaterialInstance;
class MorphTargetBuffer;
class Renderer;
class SkinningBuffer;
class VertexBuffer;
class Texture;
class FEngine;
class FRenderPrimitive;
@@ -105,8 +107,8 @@ public:
* Clients can specify bones either using this quat-vec3 pair, or by using 4x4 matrices.
*/
struct Bone {
math::quatf unitQuaternion = { 1, 0, 0, 0 };
math::float3 translation = { 0, 0, 0 };
math::quatf unitQuaternion = { 1.f, 0.f, 0.f, 0.f };
math::float3 translation = { 0.f, 0.f, 0.f };
float reserved = 0;
};
@@ -300,9 +302,6 @@ public:
/**
* Controls if the renderable has vertex morphing targets, false by default.
*
* This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer
* must provide data in the appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc).
*
* See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis
* to advance the animation.
*/
@@ -456,12 +455,16 @@ public:
/**
* Updates the vertex morphing weights on a renderable, all zeroes by default.
*
* This is specified using a 4-tuple, one float per morph target. If the renderable has fewer
* than 4 morph targets, then clients should fill the unused components with zeroes.
*
* The renderable must be built with morphing enabled, see Builder::morphing().
*/
void setMorphWeights(Instance instance, math::float4 const& weights) noexcept;
void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept;
/**
* Associates a MorphTargetBuffer to the given primitive.
*/
void setMorphTargetBufferAt(Instance instance,
size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer) noexcept;
/**
* Gets the bounding box used for frustum culling.

View File

@@ -99,7 +99,7 @@ public:
* headRoomRatio: additional headroom for the GPU as a ratio of the targetFrameTime.
* Useful for taking into account constant costs like post-processing or
* GPU drivers on different platforms.
* history: History size. higher values, tend to filter more (clamped to 30)
* history: History size. higher values, tend to filter more (clamped to 31)
* scaleRate: rate at which the gpu load is adjusted to reach the target frame rate
* This value can be computed as 1 / N, where N is the number of frames
* needed to reach 64% of the target scale factor.
@@ -110,10 +110,10 @@ public:
*
*/
struct FrameRateOptions {
float headRoomRatio = 0.0f; //!< additional headroom for the GPU
float scaleRate = 0.125f; //!< rate at which the system reacts to load changes
uint8_t history = 3; //!< history size
uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate
float headRoomRatio = 0.0f; //!< additional headroom for the GPU
float scaleRate = 1.0f / 8.0f; //!< rate at which the system reacts to load changes
uint8_t history = 15; //!< history size
uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate
};
/**
@@ -252,7 +252,7 @@ public:
*
* render() generates commands for each of the following stages:
*
* 1. Shadow map pass, if needed (currently only a single shadow map is supported).
* 1. Shadow map passes, if needed.
* 2. Depth pre-pass.
* 3. Color pass.
* 4. Post-processing pass.
@@ -341,7 +341,7 @@ public:
*
* Framebuffer as seen on User buffer (PixelBufferDescriptor&)
* screen
*
*
* +--------------------+
* | | .stride .alignment
* | | ----------------------->-->
@@ -359,7 +359,8 @@ public:
* O------------+-------+
*
*
* Typically readPixels() will be called after render() and before endFrame().
* readPixels() must be called within a frame, meaning after beginFrame() and before endFrame().
* Typically, readPixels() will be called after render().
*
* After issuing this method, the callback associated with `buffer` will be invoked on the
* main thread, indicating that the read-back has completed. Typically, this will happen

View File

@@ -22,6 +22,7 @@
#include <backend/DriverEnums.h>
#include <backend/PixelBufferDescriptor.h>
#include <backend/CallbackHandler.h>
#include <utils/compiler.h>
@@ -199,6 +200,18 @@ public:
*/
void setAcquiredImage(void* image, Callback callback, void* userdata) noexcept;
/**
* @see setAcquiredImage(void*, Callback, void*)
*
* @param image Pointer to AHardwareBuffer, casted to void* since this is a public header.
* @param handler Handler to dispatch the AcquiredImage or nullptr for the default handler.
* @param callback This is triggered by Filament when it wishes to release the image.
* It callback tales two arguments: the AHardwareBuffer and the userdata.
* @param userdata Optional closure data. Filament will pass this into the callback when it
* releases the image.
*/
void setAcquiredImage(void* image, backend::CallbackHandler* handler, Callback callback, void* userdata) noexcept;
/**
* Updates the size of the incoming stream. Whether this value is used is
* stream dependent. On Android, it must be set when using

View File

@@ -74,7 +74,7 @@ struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
LinearToneMapper() noexcept;
~LinearToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept;
math::float3 operator()(math::float3 c) const noexcept override;
};
/**
@@ -86,7 +86,7 @@ struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
ACESToneMapper() noexcept;
~ACESToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept;
math::float3 operator()(math::float3 c) const noexcept override;
};
/**
@@ -99,7 +99,7 @@ struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
ACESLegacyToneMapper() noexcept;
~ACESLegacyToneMapper() noexcept final;
math::float3 operator()(math::float3 c) const noexcept;
math::float3 operator()(math::float3 c) const noexcept override;
};
/**
@@ -112,7 +112,7 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
FilmicToneMapper() noexcept;
~FilmicToneMapper() noexcept final;
math::float3 operator()(math::float3 x) const noexcept;
math::float3 operator()(math::float3 x) const noexcept override;
};
/**
@@ -123,8 +123,6 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
*
* The tone mapping curve is defined by 5 parameters:
* - contrast: controls the contrast of the curve
* - shoulder: controls the shoulder of the curve, i.e. how quickly scene
* referred values map to output white
* - midGrayIn: sets the input middle gray
* - midGrayOut: sets the output middle gray
* - hdrMax: defines the maximum input value that will be mapped to
@@ -138,18 +136,15 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
*
* @param contrast: controls the contrast of the curve, must be > 0.0, values
* in the range 0.5..2.0 are recommended.
* @param shoulder: controls the shoulder of the curve, i.e. how quickly scene
* referred values map to output white, between 0.0 and 1.0.
* @param midGrayIn: sets the input middle gray, between 0.0 and 1.0.
* @param midGrayOut: sets the output middle gray, between 0.0 and 1.0.
* @param hdrMax: defines the maximum input value that will be mapped to
* output white. Must be >= 1.0.
*/
GenericToneMapper(
float contrast = 1.585f,
float shoulder = 0.5f,
explicit GenericToneMapper(
float contrast = 1.55f,
float midGrayIn = 0.18f,
float midGrayOut = 0.268f,
float midGrayOut = 0.215f,
float hdrMax = 10.0f
) noexcept;
~GenericToneMapper() noexcept final;
@@ -157,9 +152,9 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
GenericToneMapper(GenericToneMapper const&) = delete;
GenericToneMapper& operator=(GenericToneMapper const&) = delete;
GenericToneMapper(GenericToneMapper&& rhs) noexcept;
GenericToneMapper& operator=(GenericToneMapper& rhs) noexcept;
GenericToneMapper& operator=(GenericToneMapper&& rhs) noexcept;
math::float3 operator()(math::float3 x) const noexcept;
math::float3 operator()(math::float3 x) const noexcept override;
/** Returns the contrast of the curve as a strictly positive value. */
float getContrast() const noexcept;
@@ -179,9 +174,6 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
/** Sets the contrast of the curve, must be > 0.0, values in the range 0.5..2.0 are recommended. */
void setContrast(float contrast) noexcept;
/** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */
void setShoulder(float shoulder) noexcept;
/** Sets the input middle gray, between 0.0 and 1.0. */
void setMidGrayIn(float midGrayIn) noexcept;
@@ -225,9 +217,9 @@ private:
*/
struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper {
DisplayRangeToneMapper() noexcept;
~DisplayRangeToneMapper() noexcept;
~DisplayRangeToneMapper() noexcept override;
math::float3 operator()(math::float3 c) const noexcept;
math::float3 operator()(math::float3 c) const noexcept override;
};
} // namespace filament

View File

@@ -32,6 +32,10 @@
namespace filament {
namespace backend {
class CallbackHandler;
} // namespace backend
class Camera;
class ColorGrading;
class MaterialInstance;
@@ -76,7 +80,10 @@ public:
using RenderQuality = RenderQuality;
using AmbientOcclusionOptions = AmbientOcclusionOptions;
using TemporalAntiAliasingOptions = TemporalAntiAliasingOptions;
using MultiSampleAntiAliasingOptions = MultiSampleAntiAliasingOptions;
using VsmShadowOptions = VsmShadowOptions;
using SoftShadowOptions = SoftShadowOptions;
using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions;
/**
* Sets the View's name. Only useful for debugging.
@@ -275,7 +282,9 @@ public:
* cost. See setAntialiasing.
*
* @see setAntialiasing
* @deprecated use setMultiSampleAntiAliasingOptions instead
*/
UTILS_DEPRECATED
void setSampleCount(uint8_t count = 1) noexcept;
/**
@@ -283,7 +292,9 @@ public:
* A value of 0 or 1 means MSAA is disabled.
*
* @return value set by setSampleCount().
* @deprecated use getMultiSampleAntiAliasingOptions instead
*/
UTILS_DEPRECATED
uint8_t getSampleCount() const noexcept;
/**
@@ -320,6 +331,34 @@ public:
*/
TemporalAntiAliasingOptions const& getTemporalAntiAliasingOptions() const noexcept;
/**
* Enables or disable screen-space reflections. Disabled by default.
*
* @param options screen-space reflections options
*/
void setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept;
/**
* Returns screen-space reflections options.
*
* @return screen-space reflections options
*/
ScreenSpaceReflectionsOptions const& getScreenSpaceReflectionsOptions() const noexcept;
/**
* Enables or disable multi-sample anti-aliasing (MSAA). Disabled by default.
*
* @param options multi-sample anti-aliasing options
*/
void setMultiSampleAntiAliasingOptions(MultiSampleAntiAliasingOptions options) noexcept;
/**
* Returns multi-sample anti-aliasing options.
*
* @return multi-sample anti-aliasing options
*/
MultiSampleAntiAliasingOptions const& getMultiSampleAntiAliasingOptions() const noexcept;
/**
* Sets this View's color grading transforms.
*
@@ -514,19 +553,44 @@ public:
*/
VsmShadowOptions getVsmShadowOptions() const noexcept;
/**
* Sets soft shadowing options that apply across the entire View.
*
* Additional light-specific soft shadow parameters can be set with LightManager::setShadowOptions.
*
* Only applicable when shadow type is set to ShadowType::DPCF or ShadowType::PCSS.
*
* @param options Options for shadowing.
*
* @see setShadowType
*
* @warning This API is still experimental and subject to change.
*/
void setSoftShadowOptions(SoftShadowOptions const& options) noexcept;
/**
* Returns the soft shadowing options associated with this View.
*
* @return value set by setSoftShadowOptions().
*/
SoftShadowOptions getSoftShadowOptions() const noexcept;
/**
* Enables or disables post processing. Enabled by default.
*
* Post-processing includes:
* - Depth-of-field
* - Bloom
* - Tone-mapping & gamma encoding
* - Vignetting
* - Temporal Anti-aliasing (TAA)
* - Color grading & gamma encoding
* - Dithering
* - MSAA
* - FXAA
* - Dynamic scaling
*
* Disabling post-processing forgoes color correctness as well as anti-aliasing and
* should only be used experimentally (e.g., for UI overlays).
* Disabling post-processing forgoes color correctness as well as some anti-aliasing techniques
* and should only be used for debugging, UI overlays or when using custom render targets
* (see RenderTarget).
*
* @param enabled true enables post processing, false disables it.
*
@@ -611,9 +675,10 @@ public:
* @param x Horizontal coordinate to query in the viewport with origin on the left.
* @param y Vertical coordinate to query on the viewport with origin at the bottom.
* @param data A pointer to an instance of T
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T, void(T::*method)(PickingQueryResult const&)>
void pick(uint32_t x, uint32_t y, T* instance) noexcept {
void pick(uint32_t x, uint32_t y, T* instance, backend::CallbackHandler* handler = nullptr) noexcept {
PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) {
void* user = pq->storage;
(*static_cast<T**>(user)->*method)(result);
@@ -630,9 +695,10 @@ public:
* @param x Horizontal coordinate to query in the viewport with origin on the left.
* @param y Vertical coordinate to query on the viewport with origin at the bottom.
* @param data An instance of T
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T, void(T::*method)(PickingQueryResult const&)>
void pick(uint32_t x, uint32_t y, T instance) noexcept {
void pick(uint32_t x, uint32_t y, T instance, backend::CallbackHandler* handler = nullptr) noexcept {
static_assert(sizeof(instance) <= sizeof(PickingQuery::storage), "user data too large");
PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) {
void* user = pq->storage;
@@ -650,11 +716,12 @@ public:
* @param x Horizontal coordinate to query in the viewport with origin on the left.
* @param y Vertical coordinate to query on the viewport with origin at the bottom.
* @param functor A functor, typically a lambda function.
* @param handler Handler to dispatch the callback or nullptr for the default handler.
*/
template<typename T>
void pick(uint32_t x, uint32_t y, T functor) noexcept {
void pick(uint32_t x, uint32_t y, T functor, backend::CallbackHandler* handler = nullptr) noexcept {
static_assert(sizeof(functor) <= sizeof(PickingQuery::storage), "functor too large");
PickingQuery& query = pick(x, y,
PickingQuery& query = pick(x, y, handler,
(PickingQueryResultCallback)[](PickingQueryResult const& result, PickingQuery* pq) {
void* user = pq->storage;
T& that = *static_cast<T*>(user);
@@ -674,11 +741,12 @@ public:
* @param x Horizontal coordinate to query in the viewport with origin on the left.
* @param y Vertical coordinate to query on the viewport with origin at the bottom.
* @param callback User callback, called when the picking query result is available.
* @param handler Handler to dispatch the callback or nullptr for the default handler.
* @return A reference to a PickingQuery structure, which can be used to store up to
* 8*sizeof(void*) bytes of user data. This user data is later accessible
* in the PickingQueryResultCallback callback 3rd parameter.
*/
PickingQuery& pick(uint32_t x, uint32_t y,
PickingQuery& pick(uint32_t x, uint32_t y, backend::CallbackHandler* handler,
PickingQueryResultCallback callback) noexcept;

View File

@@ -1,120 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_FILAMESHIO_MESHREADER_H
#define TNT_FILAMENT_FILAMESHIO_MESHREADER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/CString.h>
namespace filament {
class Engine;
class VertexBuffer;
class IndexBuffer;
class MaterialInstance;
}
namespace utils {
class Path;
}
namespace filamesh {
/**
* This API can be used to read meshes stored in the "filamesh" format produced
* by the command line tool of the same name. This file format is documented in
* "docs/filamesh.md" in the Filament distribution.
*/
class UTILS_PUBLIC MeshReader {
public:
using Callback = void(*)(void* buffer, size_t size, void* user);
// Class to track material instances
class MaterialRegistry {
public:
MaterialRegistry();
MaterialRegistry(const MaterialRegistry& rhs);
MaterialRegistry& operator=(const MaterialRegistry& rhs);
~MaterialRegistry();
MaterialRegistry(MaterialRegistry&&);
MaterialRegistry& operator=(MaterialRegistry&&);
filament::MaterialInstance* getMaterialInstance(const utils::CString& name);
void registerMaterialInstance(const utils::CString& name,
filament::MaterialInstance* materialInstance);
void unregisterMaterialInstance(const utils::CString& name);
void unregisterAll();
std::size_t numRegistered() const noexcept;
void getRegisteredMaterials(filament::MaterialInstance** materialList,
utils::CString* materialNameList) const;
void getRegisteredMaterials(filament::MaterialInstance** materialList) const;
void getRegisteredMaterialNames(utils::CString* materialNameList) const;
private:
struct MaterialRegistryImpl;
MaterialRegistryImpl* mImpl;
};
struct Mesh {
utils::Entity renderable;
filament::VertexBuffer* vertexBuffer = nullptr;
filament::IndexBuffer* indexBuffer = nullptr;
};
/**
* Loads a filamesh renderable from the specified file. The material registry
* can be used to provide named materials. If a material found in the filamesh
* file cannot be matched to a material in the registry, a default material is
* used instead. The default material can be overridden by adding a material
* named "DefaultMaterial" to the registry.
*/
static Mesh loadMeshFromFile(filament::Engine* engine,
const utils::Path& path,
MaterialRegistry& materials);
/**
* Loads a filamesh renderable from an in-memory buffer. The material registry
* can be used to provide named materials. If a material found in the filamesh
* file cannot be matched to a material in the registry, a default material is
* used instead. The default material can be overridden by adding a material
* named "DefaultMaterial" to the registry.
*/
static Mesh loadMeshFromBuffer(filament::Engine* engine,
void const* data, Callback destructor, void* user,
MaterialRegistry& materials);
/**
* Loads a filamesh renderable from an in-memory buffer. The material registry
* can be used to provide named materials. All the primitives of the decoded
* renderable are assigned the specified default material.
*/
static Mesh loadMeshFromBuffer(filament::Engine* engine,
void const* data, Callback destructor, void* user,
filament::MaterialInstance* defaultMaterial);
};
} // namespace filamesh
#endif // TNT_FILAMENT_FILAMESHIO_MESHREADER_H

View File

@@ -1,131 +0,0 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_GEOMETRY_SURFACEORIENTATION_H
#define TNT_GEOMETRY_SURFACEORIENTATION_H
#include <math/quat.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
namespace filament {
/**
* Mesh-related utilities.
*/
namespace geometry {
struct OrientationBuilderImpl;
struct OrientationImpl;
/**
* The surface orientation helper can be used to populate Filament-style TANGENTS buffers.
*/
class UTILS_PUBLIC SurfaceOrientation {
public:
/**
* The Builder is used to construct an immutable surface orientation helper.
*
* Clients provide pointers into their own data, which is synchronously consumed during build().
* At a minimum, clients must supply a vertex count. They can supply data in any of the
* following combinations:
*
* 1. normals only ........................... not recommended, selects arbitrary orientation
* 2. normals + tangents ..................... sign of W determines bitangent orientation
* 3. normals + uvs + positions + indices .... selects Lengyels Method
* 4. positions + indices .................... generates normals for flat shading only
*
* Additionally, the client-side data has the following type constraints:
*
* - Normals must be float3
* - Tangents must be float4
* - UVs must be float2
* - Positions must be float3
* - Triangles must be uint3 or ushort3
*
* Currently, mikktspace is not supported because it requires re-indexing the mesh. Instead
* we use the method described by Eric Lengyel in "Foundations of Game Engine Development"
* (Volume 2, Chapter 7).
*/
class Builder {
public:
Builder() noexcept;
~Builder() noexcept;
Builder(Builder&& that) noexcept;
Builder& operator=(Builder&& that) noexcept;
/**
* This attribute is required.
*/
Builder& vertexCount(size_t vertexCount) noexcept;
Builder& normals(const filament::math::float3*, size_t stride = 0) noexcept;
Builder& tangents(const filament::math::float4*, size_t stride = 0) noexcept;
Builder& uvs(const filament::math::float2*, size_t stride = 0) noexcept;
Builder& positions(const filament::math::float3*, size_t stride = 0) noexcept;
Builder& triangleCount(size_t triangleCount) noexcept;
Builder& triangles(const filament::math::uint3*) noexcept;
Builder& triangles(const filament::math::ushort3*) noexcept;
/**
* Generates quats or returns null if the submitted data is an incomplete combination.
*/
SurfaceOrientation* build();
private:
OrientationBuilderImpl* mImpl;
Builder(const Builder&) = delete;
Builder& operator=(const Builder&) = delete;
};
~SurfaceOrientation() noexcept;
SurfaceOrientation(SurfaceOrientation&& that) noexcept;
SurfaceOrientation& operator=(SurfaceOrientation&& that) noexcept;
/**
* Returns the vertex count.
*/
size_t getVertexCount() const noexcept;
/**
* Converts quaternions into the desired output format and writes up to "quatCount"
* to the given output pointer. Normally quatCount should be equal to the vertex count.
* The optional stride is the desired quat-to-quat stride in bytes.
* @{
*/
void getQuats(filament::math::quatf* out, size_t quatCount, size_t stride = 0) const noexcept;
void getQuats(filament::math::short4* out, size_t quatCount, size_t stride = 0) const noexcept;
void getQuats(filament::math::quath* out, size_t quatCount, size_t stride = 0) const noexcept;
/**
* @}
*/
private:
SurfaceOrientation(OrientationImpl*) noexcept;
SurfaceOrientation(const SurfaceOrientation&) = delete;
SurfaceOrientation& operator=(const SurfaceOrientation&) = delete;
OrientationImpl* mImpl;
friend struct OrientationBuilderImpl;
};
} // namespace geometry
} // namespace filament
#endif // TNT_GEOMETRY_SURFACEORIENTATION_H

View File

@@ -1,104 +0,0 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_GEOMETRY_TRANSCODER_H
#define TNT_GEOMETRY_TRANSCODER_H
#include <utils/compiler.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace geometry {
enum class ComponentType {
BYTE, //!< If normalization is enabled, this maps from [-127,127] to [-1,+1]
UBYTE, //!< If normalization is enabled, this maps from [0,255] to [0, +1]
SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1]
USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1]
HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits.
};
/**
* Creates a function object that can convert vertex attribute data into tightly packed floats.
*
* This is especially useful for 3-component formats which are not supported by all backends.
* e.g. The Vulkan minspec includes float3 but not short3.
*
* Usage Example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* using filament::geometry::Transcoder;
* using filament::geometry::ComponentType;
*
* Transcoder transcode({
* .componentType = ComponentType::BYTE,
* .normalized = true,
* .componentCount = 3,
* .inputStrideBytes = 0
* });
*
* transcode(outputPtr, inputPtr, count);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* The interpretation of signed normalized data is consistent with Vulkan and OpenGL ES 3.0+.
* Note that this slightly differs from earlier versions of OpenGL ES. For example, a signed byte
* value of -127 maps exactly to -1.0f under ES3 and VK rules, but not ES2.
*/
class UTILS_PUBLIC Transcoder {
public:
/**
* Describes the format of all input data that get passed to this transcoder object.
*/
struct Config {
ComponentType componentType;
bool normalized;
uint32_t componentCount;
uint32_t inputStrideBytes = 0; //!< If stride is 0, the transcoder assumes tight packing.
};
/**
* Creates an immutable function object with the specified configuration.
*
* The config is not passed by const reference to allow for type inference at the call site.
*/
Transcoder(Config config) noexcept : mConfig(config) {}
/**
* Converts arbitrary data into tightly packed 32-bit floating point values.
*
* If target is non-null, writes up to "count" items into target and returns the number of bytes
* actually written.
*
* If target is null, returns the number of bytes required.
*
* @param target Client owned area to write into, or null for a size query
* @param source Pointer to the data to read from (does not get retained)
* @param count The maximum number of items to write (i.e. number of float3 values, not bytes)
* @return Number of bytes required to contain "count" items after conversion to packed floats
*
*/
size_t operator()(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source,
size_t count) const noexcept;
private:
const Config mConfig;
};
} // namespace geometry
} // namespace filament
#endif // TNT_GEOMETRY_TRANSCODER_H

View File

@@ -203,6 +203,16 @@ public:
*/
Animator* getAnimator() noexcept;
/**
* Updates the morphing weights in the given entity.
*/
void setMorphWeights(utils::Entity entity, const float* weights, size_t count);
/**
* Gets the number of morphing in the given entity.
*/
int getMorphTargetCount(utils::Entity entity) noexcept;
/**
* Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy
* for diagnostic purposes. The wireframe is owned by the asset so clients should not delete it.

View File

@@ -25,7 +25,7 @@
// For emscripten and Android builds, we never load from the file
// system, so we-opt out of the stdio functionality in stb.
#if defined(__EMSCRIPTEN__) || defined(ANDROID)
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__)
#define STBI_NO_STDIO
#endif

View File

@@ -52,6 +52,10 @@ struct ResourceConfiguration {
//! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files
//! do not need this, but it is useful for robustness.
bool recomputeBoundingBoxes;
//! If true, ignore skinned primitives bind transform when compute bounding box. Implicitly true
//! for instanced asset. Only applicable when recomputeBoundingBoxes is set to true
bool ignoreBindTransform;
};
/**

View File

@@ -1,46 +0,0 @@
#ifndef GLTFRESOURCES_H_
#define GLTFRESOURCES_H_
#include <stdint.h>
extern "C" {
extern const uint8_t GLTFRESOURCES_PACKAGE[];
extern int GLTFRESOURCES_LIT_FADE_OFFSET;
extern int GLTFRESOURCES_LIT_FADE_SIZE;
extern int GLTFRESOURCES_LIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_LIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_LIT_MASKED_OFFSET;
extern int GLTFRESOURCES_LIT_MASKED_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_SIZE;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET;
extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_SIZE;
extern int GLTFRESOURCES_UNLIT_FADE_OFFSET;
extern int GLTFRESOURCES_UNLIT_FADE_SIZE;
extern int GLTFRESOURCES_UNLIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_UNLIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_UNLIT_MASKED_OFFSET;
extern int GLTFRESOURCES_UNLIT_MASKED_SIZE;
extern int GLTFRESOURCES_LIT_VOLUME_OFFSET;
extern int GLTFRESOURCES_LIT_VOLUME_SIZE;
extern int GLTFRESOURCES_LIT_TRANSMISSION_OFFSET;
extern int GLTFRESOURCES_LIT_TRANSMISSION_SIZE;
extern int GLTFRESOURCES_LIT_SHEEN_OFFSET;
extern int GLTFRESOURCES_LIT_SHEEN_SIZE;
}
#define GLTFRESOURCES_LIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_FADE_OFFSET)
#define GLTFRESOURCES_LIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_LIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_MASKED_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET)
#define GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET)
#define GLTFRESOURCES_UNLIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_FADE_OFFSET)
#define GLTFRESOURCES_UNLIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_UNLIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_MASKED_OFFSET)
#define GLTFRESOURCES_LIT_VOLUME_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_VOLUME_OFFSET)
#define GLTFRESOURCES_LIT_TRANSMISSION_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_TRANSMISSION_OFFSET)
#define GLTFRESOURCES_LIT_SHEEN_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_SHEEN_OFFSET)
#endif

View File

@@ -1,16 +0,0 @@
#ifndef GLTFRESOURCES_LITE_H_
#define GLTFRESOURCES_LITE_H_
#include <stdint.h>
extern "C" {
extern const uint8_t GLTFRESOURCES_LITE_PACKAGE[];
extern int GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET;
extern int GLTFRESOURCES_LITE_LIT_OPAQUE_SIZE;
extern int GLTFRESOURCES_LITE_LIT_FADE_OFFSET;
extern int GLTFRESOURCES_LITE_LIT_FADE_SIZE;
}
#define GLTFRESOURCES_LITE_LIT_OPAQUE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET)
#define GLTFRESOURCES_LITE_LIT_FADE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_FADE_OFFSET)
#endif

View File

@@ -1,199 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAP_H
#define IBL_CUBEMAP_H
#include <ibl/Image.h>
#include <utils/compiler.h>
#include <math/vec4.h>
#include <math/vec3.h>
#include <math/vec2.h>
#include <algorithm>
namespace filament {
namespace ibl {
/**
* Generic cubemap class. It handles writing / reading into the 6 faces of a cubemap.
*
* Seamless trilinear filtering is handled.
*
* This class doesn't own the face data, it's just a "view" on the 6 images.
*
* @see CubemapUtils
*
*/
class UTILS_PUBLIC Cubemap {
public:
/**
* Initialize the cubemap with a given size, but no face is set and no memory is allocated.
*
* Usually Cubemaps are created using CubemapUtils.
*
* @see CubemapUtils
*/
explicit Cubemap(size_t dim);
Cubemap(Cubemap&&) = default;
Cubemap& operator=(Cubemap&&) = default;
~Cubemap();
enum class Face : uint8_t {
PX = 0, // left +----+
NX, // right | PY |
PY, // bottom +----+----+----+----+
NY, // top | NX | PZ | PX | NZ |
PZ, // back +----+----+----+----+
NZ // front | NY |
// +----+
};
using Texel = filament::math::float3;
//! releases all images and reset the cubemap size
void resetDimensions(size_t dim);
//! assigns an image to a face.
void setImageForFace(Face face, const Image& image);
//! retrieves the image attached to a face
inline const Image& getImageForFace(Face face) const;
//! retrieves the image attached to a face
inline Image& getImageForFace(Face face);
//! computes the center of a pixel at coordinate x, y
static inline filament::math::float2 center(size_t x, size_t y);
//! computes a direction vector from a face and a location of the center of pixel in an Image
inline filament::math::float3 getDirectionFor(Face face, size_t x, size_t y) const;
//! computes a direction vector from a face and a location in pixel in an Image
inline filament::math::float3 getDirectionFor(Face face, float x, float y) const;
//! samples the cubemap at the given direction using nearest neighbor filtering
inline Texel const& sampleAt(const filament::math::float3& direction) const;
//! samples the cubemap at the given direction using bilinear filtering
inline Texel filterAt(const filament::math::float3& direction) const;
//! samples an image at the given location in pixel using bilinear filtering
static Texel filterAt(const Image& image, float x, float y);
static Texel filterAtCenter(const Image& image, size_t x, size_t y);
//! samples two cubemaps in a given direction and lerps the result by a given lerp factor
static Texel trilinearFilterAt(const Cubemap& c0, const Cubemap& c1, float lerp,
const filament::math::float3& direction);
//! reads a texel at a given address
inline static const Texel& sampleAt(void const* data) {
return *static_cast<Texel const*>(data);
}
//! writes a texel at a given address
inline static void writeAt(void* data, const Texel& texel) {
*static_cast<Texel*>(data) = texel;
}
//! returns the size of the cubemap in pixels
size_t getDimensions() const;
/**
* Prepares a cubemap for seamless access to its faces.
*
* @warning All faces of the cubemap must be backed-up by the same Image, and must already
* be spaced by 2 lines/rows.
*/
void makeSeamless();
struct Address {
Face face;
float s = 0;
float t = 0;
};
//! returns the face and texture coordinates of the given direction
static Address getAddressFor(const filament::math::float3& direction);
private:
size_t mDimensions = 0;
float mScale = 1;
float mUpperBound = 0;
Image mFaces[6];
};
// ------------------------------------------------------------------------------------------------
inline const Image& Cubemap::getImageForFace(Face face) const {
return mFaces[int(face)];
}
inline Image& Cubemap::getImageForFace(Face face) {
return mFaces[int(face)];
}
inline filament::math::float2 Cubemap::center(size_t x, size_t y) {
return { x + 0.5f, y + 0.5f };
}
inline filament::math::float3 Cubemap::getDirectionFor(Face face, size_t x, size_t y) const {
return getDirectionFor(face, x + 0.5f, y + 0.5f);
}
inline filament::math::float3 Cubemap::getDirectionFor(Face face, float x, float y) const {
// map [0, dim] to [-1,1] with (-1,-1) at bottom left
float cx = (x * mScale) - 1;
float cy = 1 - (y * mScale);
filament::math::float3 dir;
const float l = std::sqrt(cx * cx + cy * cy + 1);
switch (face) {
case Face::PX: dir = { 1, cy, -cx }; break;
case Face::NX: dir = { -1, cy, cx }; break;
case Face::PY: dir = { cx, 1, -cy }; break;
case Face::NY: dir = { cx, -1, cy }; break;
case Face::PZ: dir = { cx, cy, 1 }; break;
case Face::NZ: dir = { -cx, cy, -1 }; break;
}
return dir * (1 / l);
}
inline Cubemap::Texel const& Cubemap::sampleAt(const filament::math::float3& direction) const {
Cubemap::Address addr(getAddressFor(direction));
const size_t x = std::min(size_t(addr.s * mDimensions), mDimensions - 1);
const size_t y = std::min(size_t(addr.t * mDimensions), mDimensions - 1);
return sampleAt(getImageForFace(addr.face).getPixelRef(x, y));
}
inline Cubemap::Texel Cubemap::filterAt(const filament::math::float3& direction) const {
Cubemap::Address addr(getAddressFor(direction));
addr.s = std::min(addr.s * mDimensions, mUpperBound);
addr.t = std::min(addr.t * mDimensions, mUpperBound);
return filterAt(getImageForFace(addr.face), addr.s, addr.t);
}
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAP_H */

View File

@@ -1,91 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAPIBL_H
#define IBL_CUBEMAPIBL_H
#include <math/vec3.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <vector>
#include <stdint.h>
#include <stddef.h>
namespace utils {
class JobSystem;
} // namespace utils
namespace filament {
namespace ibl {
class Cubemap;
class Image;
/**
* Generates cubemaps for the IBL.
*/
class UTILS_PUBLIC CubemapIBL {
public:
typedef void (*Progress)(size_t, float, void*);
/**
* Computes a roughness LOD using prefiltered importance sampling GGX
*
* @param dst the destination cubemap
* @param levels a list of prefiltered lods of the source environment
* @param linearRoughness roughness
* @param maxNumSamples number of samples for importance sampling
* @param updater a callback for the caller to track progress
*/
static void roughnessFilter(
utils::JobSystem& js, Cubemap& dst, const utils::Slice<Cubemap>& levels,
float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter,
Progress updater = nullptr, void* userdata = nullptr);
static void roughnessFilter(
utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter,
Progress updater = nullptr, void* userdata = nullptr);
//! Computes the "DFG" term of the "split-sum" approximation and stores it in a 2D image
static void DFG(utils::JobSystem& js, Image& dst, bool multiscatter, bool cloth);
/**
* Computes the diffuse irradiance using prefiltered importance sampling GGX
*
* @note Usually this is done using spherical harmonics instead.
*
* @param dst the destination cubemap
* @param levels a list of prefiltered lods of the source environment
* @param maxNumSamples number of samples for importance sampling
* @param updater a callback for the caller to track progress
*
* @see CubemapSH
*/
static void diffuseIrradiance(utils::JobSystem& js, Cubemap& dst, const std::vector<Cubemap>& levels,
size_t maxNumSamples = 1024, Progress updater = nullptr, void* userdata = nullptr);
// for debugging. ignore.
static void brdf(utils::JobSystem& js, Cubemap& dst, float linearRoughness);
};
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAPIBL_H */

View File

@@ -1,125 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAPSH_H
#define IBL_CUBEMAPSH_H
#include <utils/compiler.h>
#include <math/mat3.h>
#include <math/vec3.h>
#include <memory>
#include <vector>
namespace utils {
class JobSystem;
} // namespace utils
namespace filament {
namespace ibl {
class Cubemap;
/**
* Computes spherical harmonics
*/
class UTILS_PUBLIC CubemapSH {
public:
/**
* Spherical Harmonics decomposition of the given cubemap
* Optionally calculates irradiance by convolving with truncated cos.
*/
static std::unique_ptr<math::float3[]> computeSH(
utils::JobSystem& js, const Cubemap& cm, size_t numBands, bool irradiance);
/**
* Render given spherical harmonics into a cubemap
*/
static void renderSH(utils::JobSystem& js, Cubemap& cm,
const std::unique_ptr<math::float3[]>& sh, size_t numBands);
static void windowSH(std::unique_ptr<math::float3[]>& sh, size_t numBands, float cutoff);
/**
* Compute spherical harmonics of the irradiance of the given cubemap.
* The SH basis are pre-scaled for easier rendering by the shader. The resulting coefficients
* are not spherical harmonics (as they're scalled by various factors). In particular they
* cannot be rendered with renderSH() above. Instead use renderPreScaledSH3Bands() which
* is exactly the code ran by our shader.
*/
static void preprocessSHForShader(std::unique_ptr<math::float3[]>& sh);
/**
* Render pre-scaled irrandiance SH
*/
static void renderPreScaledSH3Bands(utils::JobSystem& js, Cubemap& cm,
const std::unique_ptr<math::float3[]>& sh);
static constexpr size_t getShIndex(ssize_t m, size_t l) {
return SHindex(m, l);
}
private:
class float5 {
float v[5];
public:
float5() = default;
constexpr float5(float a, float b, float c, float d, float e) : v{ a, b, c, d, e } {}
constexpr float operator[](size_t i) const { return v[i]; }
float& operator[](size_t i) { return v[i]; }
};
static inline const float5 multiply(const float5 M[5], float5 x) noexcept {
return float5{
M[0][0] * x[0] + M[1][0] * x[1] + M[2][0] * x[2] + M[3][0] * x[3] + M[4][0] * x[4],
M[0][1] * x[0] + M[1][1] * x[1] + M[2][1] * x[2] + M[3][1] * x[3] + M[4][1] * x[4],
M[0][2] * x[0] + M[1][2] * x[1] + M[2][2] * x[2] + M[3][2] * x[3] + M[4][2] * x[4],
M[0][3] * x[0] + M[1][3] * x[1] + M[2][3] * x[2] + M[3][3] * x[3] + M[4][3] * x[4],
M[0][4] * x[0] + M[1][4] * x[1] + M[2][4] * x[2] + M[3][4] * x[3] + M[4][4] * x[4]
};
};
static inline constexpr size_t SHindex(ssize_t m, size_t l) {
return l * (l + 1) + m;
}
static void computeShBasis(float* SHb, size_t numBands, const math::float3& s);
static float Kml(ssize_t m, size_t l);
static std::vector<float> Ki(size_t numBands);
static constexpr float computeTruncatedCosSh(size_t l);
static float sincWindow(size_t l, float w);
static math::float3 rotateShericalHarmonicBand1(math::float3 band1, math::mat3f const& M);
static float5 rotateShericalHarmonicBand2(float5 const& band2, math::mat3f const& M);
// debugging only...
static float Legendre(ssize_t l, ssize_t m, float x);
static float TSH(int l, int m, const math::float3& d);
static void printShBase(std::ostream& out, int l, int m);
};
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAPSH_H */

View File

@@ -1,124 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_CUBEMAP_UTILS_H
#define IBL_CUBEMAP_UTILS_H
#include <ibl/Cubemap.h>
#include <ibl/Image.h>
#include <utils/compiler.h>
#include <functional>
namespace utils {
class JobSystem;
} // namespace utils
namespace filament {
namespace ibl {
class CubemapIBL;
/**
* Create and convert Cubemap formats
*/
class UTILS_PUBLIC CubemapUtils {
public:
//! Creates a cubemap object and its backing Image
static Cubemap create(Image& image, size_t dim, bool horizontal = true);
struct EmptyState {
};
template<typename STATE>
using ScanlineProc = std::function<
void(STATE& state, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t width)>;
template<typename STATE>
using ReduceProc = std::function<void(STATE& state)>;
//! process the cubemap using multithreading
template<typename STATE>
static void process(Cubemap& cm,
utils::JobSystem& js,
ScanlineProc<STATE> proc,
ReduceProc<STATE> reduce = [](STATE&) {},
const STATE& prototype = STATE());
//! process the cubemap
template<typename STATE>
static void processSingleThreaded(Cubemap& cm,
utils::JobSystem& js,
ScanlineProc<STATE> proc,
ReduceProc<STATE> reduce = [](STATE&) {},
const STATE& prototype = STATE());
//! clamps image to acceptable range
static void clamp(Image& src);
static void highlight(Image& src);
//! Downsamples a cubemap by helf in x and y using a box filter
static void downsampleCubemapLevelBoxFilter(utils::JobSystem& js, Cubemap& dst, const Cubemap& src);
//! Return the name of a face (suitable for a file name)
static const char* getFaceName(Cubemap::Face face);
//! computes the solid angle of a pixel of a face of a cubemap
static float solidAngle(size_t dim, size_t u, size_t v);
//! Sets a Cubemap faces from a cross image
static void setAllFacesFromCross(Cubemap& cm, const Image& image);
private:
//move these into cmgen?
static void setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image);
static Image createCubemapImage(size_t dim, bool horizontal = true);
#ifndef FILAMENT_IBL_LITE
public:
//! Converts horizontal or vertical cross Image to a Cubemap
static void crossToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts equirectangular Image to a Cubemap
static void equirectangularToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src);
//! Converts a Cubemap to an equirectangular Image
static void cubemapToEquirectangular(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! Converts a Cubemap to an octahedron
static void cubemapToOctahedron(utils::JobSystem& js, Image& dst, const Cubemap& src);
//! mirror the cubemap in the horizontal direction
static void mirrorCubemap(utils::JobSystem& js, Cubemap& dst, const Cubemap& src);
//! generates a UV grid in the cubemap -- useful for debugging.
static void generateUVGrid(utils::JobSystem& js, Cubemap& cml, size_t gridFrequencyX, size_t gridFrequencyY);
#endif
friend class CubemapIBL;
};
} // namespace ibl
} // namespace filament
#endif /* IBL_CUBEMAP_UTILS_H */

View File

@@ -1,77 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_IMAGE_H
#define IBL_IMAGE_H
#include <math/scalar.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
#include <memory>
namespace filament {
namespace ibl {
class UTILS_PUBLIC Image {
public:
Image();
Image(size_t w, size_t h, size_t stride = 0);
void reset();
void set(Image const& image);
void subset(Image const& image, size_t x, size_t y, size_t w, size_t h);
bool isValid() const { return mData != nullptr; }
size_t getWidth() const { return mWidth; }
size_t getStride() const { return mBpr / getBytesPerPixel(); }
size_t getHeight() const { return mHeight; }
size_t getBytesPerRow() const { return mBpr; }
size_t getBytesPerPixel() const { return sizeof(math::float3); }
void* getData() const { return mData; }
size_t getSize() const { return mBpr * mHeight; }
void* getPixelRef(size_t x, size_t y) const;
std::unique_ptr<uint8_t[]> detach() { return std::move(mOwnedData); }
private:
size_t mBpr = 0;
size_t mWidth = 0;
size_t mHeight = 0;
std::unique_ptr<uint8_t[]> mOwnedData;
void* mData = nullptr;
};
inline void* Image::getPixelRef(size_t x, size_t y) const {
return static_cast<uint8_t*>(mData) + y * getBytesPerRow() + x * getBytesPerPixel();
}
} // namespace ibl
} // namespace filament
#endif /* IBL_IMAGE_H */

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IBL_UTILITIES_H
#define IBL_UTILITIES_H
#include <math.h>
#include <math/vec2.h>
#include <math/vec3.h>
namespace filament {
namespace ibl {
template<typename T>
static inline constexpr T sq(T x) {
return x * x;
}
template<typename T>
static inline constexpr T log4(T x) {
// log2(x)/log2(4)
// log2(x)/2
return std::log2(x) * T(0.5);
}
inline bool isPOT(size_t x) {
return !(x & (x - 1));
}
inline filament::math::float2 hammersley(uint32_t i, float iN) {
constexpr float tof = 0.5f / 0x80000000U;
uint32_t bits = i;
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return { i * iN, bits * tof };
}
} // namespace ibl
} // namespace filament
#endif /* IBL_UTILITIES_H */

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file Functions and types related to block-compressed texture formats.
#ifndef IMAGEIO_BLOCKCOMPRESSION_H_
#define IMAGEIO_BLOCKCOMPRESSION_H_
#include <image/LinearImage.h>
#include <memory>
#include <string>
#include <math/vec2.h>
#include <utils/compiler.h>
#include <stdint.h>
namespace image {
enum class CompressedFormat {
INVALID = 0,
R11_EAC = 0x9270,
SIGNED_R11_EAC = 0x9271,
RG11_EAC = 0x9272,
SIGNED_RG11_EAC = 0x9273,
RGB8_ETC2 = 0x9274,
SRGB8_ETC2 = 0x9275,
RGB8_ALPHA1_ETC2 = 0x9276,
SRGB8_ALPHA1_ETC = 0x9277,
RGBA8_ETC2_EAC = 0x9278,
SRGB8_ALPHA8_ETC2_EAC = 0x9279,
RGB_S3TC_DXT1 = 0x83F0,
RGBA_S3TC_DXT1 = 0x83F1,
RGBA_S3TC_DXT3 = 0x83F2,
RGBA_S3TC_DXT5 = 0x83F3,
SRGB_S3TC_DXT1 = 0x8C4C,
SRGB_ALPHA_S3TC_DXT1 = 0x8C4D,
SRGB_ALPHA_S3TC_DXT3 = 0x8C4E,
SRGB_ALPHA_S3TC_DXT5 = 0x8C4F,
RGBA_ASTC_4x4 = 0x93B0,
RGBA_ASTC_5x4 = 0x93B1,
RGBA_ASTC_5x5 = 0x93B2,
RGBA_ASTC_6x5 = 0x93B3,
RGBA_ASTC_6x6 = 0x93B4,
RGBA_ASTC_8x5 = 0x93B5,
RGBA_ASTC_8x6 = 0x93B6,
RGBA_ASTC_8x8 = 0x93B7,
RGBA_ASTC_10x5 = 0x93B8,
RGBA_ASTC_10x6 = 0x93B9,
RGBA_ASTC_10x8 = 0x93BA,
RGBA_ASTC_10x10 = 0x93BB,
RGBA_ASTC_12x10 = 0x93BC,
RGBA_ASTC_12x12 = 0x93BD,
SRGB8_ALPHA8_ASTC_4x4 = 0x93D0,
SRGB8_ALPHA8_ASTC_5x4 = 0x93D1,
SRGB8_ALPHA8_ASTC_5x5 = 0x93D2,
SRGB8_ALPHA8_ASTC_6x5 = 0x93D3,
SRGB8_ALPHA8_ASTC_6x6 = 0x93D4,
SRGB8_ALPHA8_ASTC_8x5 = 0x93D5,
SRGB8_ALPHA8_ASTC_8x6 = 0x93D6,
SRGB8_ALPHA8_ASTC_8x8 = 0x93D7,
SRGB8_ALPHA8_ASTC_10x5 = 0x93D8,
SRGB8_ALPHA8_ASTC_10x6 = 0x93D9,
SRGB8_ALPHA8_ASTC_10x8 = 0x93DA,
SRGB8_ALPHA8_ASTC_10x10 = 0x93DB,
SRGB8_ALPHA8_ASTC_12x10 = 0x93DC,
SRGB8_ALPHA8_ASTC_12x12 = 0x93DD,
};
// Represents the opaque result of compression and the chosen texture format.
struct CompressedTexture {
const CompressedFormat format;
const uint32_t size;
std::unique_ptr<uint8_t[]> data;
};
// ASTC ////////////////////////////////////////////////////////////////////////////////////////////
// Controls how fast compression occurs at the cost of quality in the resulting image.
enum class AstcPreset {
VERYFAST,
FAST,
MEDIUM,
THOROUGH,
EXHAUSTIVE,
};
// Informs the encoder what texels represent; this is especially crucial for normal maps.
enum class AstcSemantic {
COLORS_LDR,
COLORS_HDR,
NORMALS,
};
// The encoder configuration controls the quality and speed of compression, as well as the resulting
// format. The specified block size must be one of the 14 block sizes that can be consumed by ES 3.2
// as per https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glCompressedTexImage2D.xhtml
struct AstcConfig {
AstcPreset quality;
AstcSemantic semantic;
filament::math::ushort2 blocksize;
bool srgb;
};
// Uses the CPU to compress a linear image (1 to 4 channels) into an ASTC texture. The 16-byte
// header block that ARM uses in their file format is not included.
CompressedTexture astcCompress(const LinearImage& source, AstcConfig config);
// Parses a simple underscore-delimited string to produce an ASTC compression configuration. This
// makes it easy to incorporate the compression API into command-line tools. If the string is
// malformed, this returns a config with a 0x0 blocksize. Example strings: fast_ldr_4x4,
// thorough_normals_6x6, veryfast_hdr_12x10
AstcConfig astcParseOptionString(const std::string& options);
// ETC /////////////////////////////////////////////////////////////////////////////////////////////
enum class EtcErrorMetric {
RGBA,
RGBX,
REC709,
NUMERIC,
NORMALXYZ,
};
// Informs the ETC encoder of the desired output. Effort sets the quality / speed tradeoff with
// a number between 0 and 100.
struct EtcConfig {
CompressedFormat format;
EtcErrorMetric metric;
int effort;
};
// Uses the CPU to compress a linear image (1 to 4 channels) into an ETC texture.
CompressedTexture etcCompress(const LinearImage& source, EtcConfig config);
// Converts a string into an ETC compression configuration where the string has the form
// FORMAT_METRIC_EFFORT where:
// - FORMAT is one of: r11, signed_r11, rg11, signed_rg11, rgb8, srgb8, rgb8_alpha,
// srgb8_alpha, rgba8, and srgb8_alpha8
// - METRIC is one of: rgba, rgbx, rec709, numeric, and normalxyz
// - EFFORT is an integer between 0 and 100
EtcConfig etcParseOptionString(const std::string& options);
// S3TC ////////////////////////////////////////////////////////////////////////////////////////////
// Informs the S3TC encoder of the desired output.
struct S3tcConfig {
CompressedFormat format;
bool srgb;
};
// Uses the CPU to compress a linear image (1 to 4 channels) into an S3TC texture.
CompressedTexture s3tcCompress(const LinearImage& source, S3tcConfig config);
// Parses an underscore-delimited string to produce an S3TC compression configuration. Currently
// this only accepts "rgb_dxt1" and "rgba_dxt5". If the string is malformed, this returns a config
// with an invalid format.
S3tcConfig s3tcParseOptionString(const std::string& options);
///////////////////////////////////////////////////////////////////////////////////////////////////
struct CompressionConfig {
enum { INVALID, ASTC, S3TC, ETC } type;
AstcConfig astc;
S3tcConfig s3tc;
EtcConfig etc;
};
bool parseOptionString(const std::string& options, CompressionConfig* config);
UTILS_PUBLIC
CompressedTexture compressTexture(const CompressionConfig& config, const LinearImage& image);
} // namespace image
#endif /* IMAGEIO_BLOCKCOMPRESSION_H_ */

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_HDRDECODER_H_
#define IMAGE_HDRDECODER_H_
#include <imageio/ImageDecoder.h>
namespace image {
class HDRDecoder : public ImageDecoder::Decoder {
public:
static HDRDecoder* create(std::istream& stream);
static bool checkSignature(char const* buf);
HDRDecoder(const HDRDecoder&) = delete;
HDRDecoder& operator=(const HDRDecoder&) = delete;
private:
explicit HDRDecoder(std::istream& stream);
~HDRDecoder() override;
// ImageDecoder::Decoder interface
LinearImage decode() override;
static const char sigRadiance[];
static const char sigRGBE[];
std::istream& mStream;
std::streampos mStreamStartPos;
};
} // namespace image
#endif /* IMAGE_IMAGEDECODER_H_ */

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_IMAGEDECODER_H_
#define IMAGE_IMAGEDECODER_H_
#include <iosfwd>
#include <string>
#include <image/LinearImage.h>
#include <utils/compiler.h>
namespace image {
class UTILS_PUBLIC ImageDecoder {
public:
enum class ColorSpace {
LINEAR,
SRGB
};
// Returns linear floating-point data, or a non-valid image if an error occured.
static LinearImage decode(std::istream& stream, const std::string& sourceName,
ColorSpace sourceSpace = ColorSpace::SRGB);
class Decoder {
public:
virtual LinearImage decode() = 0;
virtual ~Decoder() = default;
ColorSpace getColorSpace() const noexcept {
return mColorSpace;
}
void setColorSpace(ColorSpace colorSpace) noexcept {
mColorSpace = colorSpace;
}
private:
ColorSpace mColorSpace = ColorSpace::SRGB;
};
private:
enum class Format {
NONE,
PNG,
HDR,
PSD,
EXR
};
};
} // namespace image
#endif /* IMAGE_IMAGEDECODER_H_ */

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <image/LinearImage.h>
#include <utils/Path.h>
namespace image {
enum class ComparisonMode {
SKIP,
COMPARE,
UPDATE,
};
// Saves an image to disk or does a load-and-compare, depending on comparison mode.
// This makes it easy for unit tests to have compare / update commands.
// The passed-in image is the "result image" and the expected image is the "golden image".
void updateOrCompare(LinearImage result, const utils::Path& golden, ComparisonMode, float epsilon);
} // namespace image

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef IMAGE_IMAGEENCODER_H_
#define IMAGE_IMAGEENCODER_H_
#include <iosfwd>
#include <string>
#include <image/LinearImage.h>
#include <utils/compiler.h>
namespace image {
class UTILS_PUBLIC ImageEncoder {
public:
enum class Format {
PNG, // 8-bit sRGB, 1 or 3 channels
PNG_LINEAR, // 8-bit linear RGB, 1 or 3 channels
HDR, // 8-bit linear RGBE, 3 channels only
RGBM, // 8-bit RGBM, as PNG, 3 channels only
PSD, // 16-bit sRGB or 32-bit linear RGB, 3 channels only
// Default: 16 bit
EXR, // 16-bit linear RGB (half-float), 3 channels only
// Default: PIZ compression
DDS, // 8-bit sRGB, 1, 2 or 3 channels;
// 16-bit or 32-bit linear RGB, 1, 2 or 3 channels
// Default: 16 bit
DDS_LINEAR, // 8-bit, 16-bit or 32-bit linear RGB, 1, 2 or 3 channels
// Default: 16 bit
RGB_10_11_11_REV, // RGBA PNG file, but containing 11_11_10 data
};
// Consumes linear floating-point data, returns false if unable to encode.
static bool encode(std::ostream& stream, Format format, const LinearImage& image,
const std::string& compression, const std::string& destName);
static Format chooseFormat(const std::string& name, bool forceLinear = false);
static std::string chooseExtension(Format format);
class Encoder {
public:
virtual bool encode(const LinearImage& image) = 0;
virtual ~Encoder() = default;
};
};
} // namespace image
#endif /* IMAGE_IMAGEENCODER_H_ */

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef MATH_TMATHELPERS_H_
#define MATH_TMATHELPERS_H_
#ifndef TNT_MATH_TMATHELPERS_H
#define TNT_MATH_TMATHELPERS_H
#include <math/compiler.h>
#include <math/quat.h>
@@ -804,4 +804,4 @@ public:
} // namespace math
} // namespace filament
#endif // MATH_TMATHELPERS_H_
#endif // TNT_MATH_TMATHELPERS_H

View File

@@ -14,18 +14,17 @@
* limitations under the License.
*/
#ifndef MATH_TQUATHELPERS_H_
#define MATH_TQUATHELPERS_H_
#include <math.h>
#include <stdint.h>
#include <sys/types.h>
#ifndef TNT_MATH_TQUATHELPERS_H
#define TNT_MATH_TQUATHELPERS_H
#include <math/compiler.h>
#include <math/scalar.h>
#include <math/vec3.h>
#include <math.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
namespace details {
@@ -289,4 +288,4 @@ public:
} // namespace math
} // namespace filament
#endif // MATH_TQUATHELPERS_H_
#endif // TNT_MATH_TQUATHELPERS_H

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef MATH_TVECHELPERS_H_
#define MATH_TVECHELPERS_H_
#ifndef TNT_MATH_TVECHELPERS_H
#define TNT_MATH_TVECHELPERS_H
#include <math/compiler.h>
@@ -477,6 +477,13 @@ private:
return v;
}
friend inline VECTOR<T> MATH_PURE sign(VECTOR<T> v) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::copysign(T(1), v[i]);
}
return v;
}
friend inline VECTOR<T> MATH_PURE pow(VECTOR<T> v, T p) {
for (size_t i = 0; i < v.size(); i++) {
v[i] = std::pow(v[i], p);
@@ -622,4 +629,4 @@ private:
} // namespace math
} // namespace filament
#endif // MATH_TVECHELPERS_H_
#endif // TNT_MATH_TVECHELPERS_H

View File

@@ -14,7 +14,8 @@
* limitations under the License.
*/
#pragma once
#ifndef TNT_MATH_COMPILER_H
#define TNT_MATH_COMPILER_H
#include <type_traits>
@@ -111,8 +112,7 @@
#endif // _MSC_VER
namespace filament {
namespace math {
namespace filament::math {
// MSVC 2019 16.4 doesn't seem to like it when we specialize std::is_arithmetic for
// filament::math::half, so we're forced to create our own is_arithmetic here and specialize it
@@ -122,5 +122,6 @@ struct is_arithmetic : std::integral_constant<bool,
std::is_integral<T>::value || std::is_floating_point<T>::value> {
};
}
}
} // filament::math
#endif // TNT_MATH_COMPILER_H

View File

@@ -17,13 +17,14 @@
#ifndef TNT_MATH_FAST_H
#define TNT_MATH_FAST_H
#include <cmath>
#include <cstdint>
#include <type_traits>
#include <math/compiler.h>
#include <math/scalar.h>
#include <cmath>
#include <type_traits>
#include <stdint.h>
#ifdef __ARM_NEON
#include <arm_neon.h>
#endif

View File

@@ -17,14 +17,14 @@
#ifndef TNT_MATH_HALF_H
#define TNT_MATH_HALF_H
#include <math/compiler.h>
#include <limits>
#include <type_traits>
#include <stddef.h>
#include <stdint.h>
#include <math/compiler.h>
namespace filament {
namespace math {

View File

@@ -14,12 +14,13 @@
* limitations under the License.
*/
#ifndef MATH_MAT2_H_
#define MATH_MAT2_H_
#ifndef TNT_MATH_MAT2_H
#define TNT_MATH_MAT2_H
#include <math/TMatHelpers.h>
#include <math/vec2.h>
#include <math/compiler.h>
#include <math/vec2.h>
#include <stdint.h>
#include <sys/types.h>
@@ -358,4 +359,4 @@ constexpr void swap(filament::math::details::TMat22<T>& lhs,
}
}
#endif // MATH_MAT2_H_
#endif // TNT_MATH_MAT2_H

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
#ifndef MATH_MAT3_H_
#define MATH_MAT3_H_
#ifndef TNT_MATH_MAT3_H
#define TNT_MATH_MAT3_H
#include <math/quat.h>
#include <math/TMatHelpers.h>
#include <math/vec3.h>
#include <math/compiler.h>
#include <math/quat.h>
#include <math/vec3.h>
#include <limits.h>
#include <stdint.h>
@@ -487,4 +487,4 @@ constexpr void swap(filament::math::details::TMat33<T>& lhs,
}
}
#endif // MATH_MAT3_H_
#endif // TNT_MATH_MAT3_H

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
#ifndef MATH_MAT4_H_
#define MATH_MAT4_H_
#ifndef TNT_MATH_MAT4_H
#define TNT_MATH_MAT4_H
#include <math/TMatHelpers.h>
#include <math/compiler.h>
#include <math/mat3.h>
#include <math/quat.h>
#include <math/scalar.h>
#include <math/TMatHelpers.h>
#include <math/vec3.h>
#include <math/vec4.h>
@@ -557,6 +557,26 @@ constexpr typename TMat44<T>::col_type MATH_PURE operator*(const TMat44<T>& lhs,
typedef details::TMat44<double> mat4;
typedef details::TMat44<float> mat4f;
// mat4 * float4, with double precision intermediates
constexpr float4 highPrecisionMultiply(mat4f const& lhs, float4 const& rhs) noexcept {
double4 result{};
result += lhs[0] * rhs[0];
result += lhs[1] * rhs[1];
result += lhs[2] * rhs[2];
result += lhs[3] * rhs[3];
return float4{ result };
}
// mat4 * mat4, with double precision intermediates
constexpr mat4f highPrecisionMultiply(mat4f const& lhs, mat4f const& rhs) noexcept {
return {
highPrecisionMultiply(lhs, rhs[0]),
highPrecisionMultiply(lhs, rhs[1]),
highPrecisionMultiply(lhs, rhs[2]),
highPrecisionMultiply(lhs, rhs[3])
};
}
// ----------------------------------------------------------------------------------------
} // namespace math
} // namespace filament
@@ -628,4 +648,4 @@ constexpr void swap(filament::math::details::TMat44<T>& lhs,
}
}
#endif // MATH_MAT4_H_
#endif // TNT_MATH_MAT4_H

View File

@@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef MATH_MATHFWD_H_
#define MATH_MATHFWD_H_
#ifndef TNT_MATH_MATHFWD_H
#define TNT_MATH_MATHFWD_H
#ifdef _MSC_VER
@@ -91,4 +91,4 @@ using mat4f = details::TMat44<float>;
#endif // _MSC_VER
#endif // MATH_MATHFWD_H_
#endif // TNT_MATH_MATHFWD_H

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
#ifndef MATH_QUAT_H_
#define MATH_QUAT_H_
#ifndef TNT_MATH_QUAT_H
#define TNT_MATH_QUAT_H
#include <math/half.h>
#include <math/TQuatHelpers.h>
#include <math/compiler.h>
#include <math/half.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/compiler.h>
#include <stdint.h>
#include <sys/types.h>
@@ -164,4 +164,4 @@ constexpr inline quat operator "" _k(unsigned long long v) {
} // namespace math
} // namespace filament
#endif // MATH_QUAT_H_
#endif // TNT_MATH_QUAT_H

View File

@@ -14,16 +14,17 @@
* limitations under the License.
*/
#ifndef MATH_VEC2_H_
#define MATH_VEC2_H_
#ifndef TNT_MATH_VEC2_H
#define TNT_MATH_VEC2_H
#include <math/TVecHelpers.h>
#include <math/half.h>
#include <type_traits>
#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <type_traits>
namespace filament {
namespace math {
@@ -110,4 +111,4 @@ using bool2 = vec2<bool>;
} // namespace math
} // namespace filament
#endif // MATH_VEC2_H_
#endif // TNT_MATH_VEC2_H

View File

@@ -14,15 +14,15 @@
* limitations under the License.
*/
#ifndef MATH_VEC3_H_
#define MATH_VEC3_H_
#ifndef TNT_MATH_VEC3_H
#define TNT_MATH_VEC3_H
#include <math/vec2.h>
#include <math/half.h>
#include <math/vec2.h>
#include <stdint.h>
#include <sys/types.h>
namespace filament {
namespace math {
// -------------------------------------------------------------------------------------
@@ -130,4 +130,4 @@ using bool3 = vec3<bool>;
} // namespace math
} // namespace filament
#endif // MATH_VEC3_H_
#endif // TNT_MATH_VEC3_H

View File

@@ -14,11 +14,12 @@
* limitations under the License.
*/
#ifndef MATH_VEC4_H_
#define MATH_VEC4_H_
#ifndef TNT_MATH_VEC4_H
#define TNT_MATH_VEC4_H
#include <math/vec3.h>
#include <math/half.h>
#include <math/vec3.h>
#include <stdint.h>
#include <sys/types.h>
@@ -129,4 +130,4 @@ using bool4 = vec4<bool>;
} // namespace math
} // namespace filament
#endif // MATH_VEC4_H_
#endif // TNT_MATH_VEC4_H

View File

@@ -1,60 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iosfwd>
#include <math/mathfwd.h>
#if __has_attribute(visibility)
# define MATHIO_PUBLIC __attribute__((visibility("default")))
#else
# define MATHIO_PUBLIC
#endif
namespace filament {
namespace math {
namespace details { template<typename T> class TQuaternion; }
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TVec2<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TVec3<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TVec4<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TMat22<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TMat33<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TMat44<T>& v) noexcept;
template<typename T>
MATHIO_PUBLIC
std::ostream& operator<<(std::ostream& out, const details::TQuaternion<T>& v) noexcept;
} // namespace math
} // namespace filament

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <camutils/Bookmark.h>
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#include <math/vec3.h>
using namespace filament::math;
namespace filament {
namespace camutils {
template <typename FLOAT>
Bookmark<FLOAT> Bookmark<FLOAT>::interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t) {
Bookmark<FLOAT> result;
using float3 = filament::math::vec3<FLOAT>;
if (a.mode == Mode::MAP) {
assert(b.mode == Mode::MAP);
const double rho = sqrt(2.0);
const double rho2 = 2, rho4 = 4;
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
const double dr = r1 - r0;
const int valid = !std::isnan(dr) && dr != 0;
const double S = (valid ? dr : log(w1 / w0)) / rho;
const double s = t * S;
// This performs Van Wijk interpolation to animate between two waypoints on a map.
if (valid) {
const double coshr0 = cosh(r0);
const double u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
Bookmark<FLOAT> result;
result.map.center.x = ux0 + u * dx;
result.map.center.y = uy0 + u * dy;
result.map.extent = w0 * coshr0 / cosh(rho * s + r0);
return result;
}
// For degenerate cases, fall back to a simplified interpolation method.
result.map.center.x = ux0 + t * dx;
result.map.center.y = uy0 + t * dy;
result.map.extent = w0 * exp(rho * s);
return result;
}
assert(b.mode == Mode::ORBIT);
result.orbit.phi = lerp(a.orbit.phi, b.orbit.phi, FLOAT(t));
result.orbit.theta = lerp(a.orbit.theta, b.orbit.theta, FLOAT(t));
result.orbit.distance = lerp(a.orbit.distance, b.orbit.distance, FLOAT(t));
result.orbit.pivot = lerp(a.orbit.pivot, b.orbit.pivot, float3(t));
return result;
}
// Uses the Van Wijk method to suggest a duration for animating between two waypoints on a map.
// This does not have units, so just use it as a multiplier.
template <typename FLOAT>
double Bookmark<FLOAT>::duration(Bookmark<FLOAT> a, Bookmark<FLOAT> b) {
assert(a.mode == Mode::ORBIT && b.mode == Mode::ORBIT);
const double rho = sqrt(2.0);
const double rho2 = 2, rho4 = 4;
const double ux0 = a.map.center.x, uy0 = a.map.center.y, w0 = a.map.extent;
const double ux1 = b.map.center.x, uy1 = b.map.center.y, w1 = b.map.extent;
const double dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = sqrt(d2);
const double b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2.0 * w0 * rho2 * d1);
const double b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2.0 * w1 * rho2 * d1);
const double r0 = log(sqrt(b0 * b0 + 1.0) - b0);
const double r1 = log(sqrt(b1 * b1 + 1) - b1);
const double dr = r1 - r0;
const int valid = !std::isnan(dr) && dr != 0;
const double S = (valid ? dr : log(w1 / w0)) / rho;
return fabs(S);
}
template class Bookmark<float>;
} // namespace camutils
} // namespace filament

View File

@@ -0,0 +1,206 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_FREEFLIGHT_MANIPULATOR_H
#define CAMUTILS_FREEFLIGHT_MANIPULATOR_H
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/quat.h>
#include <cmath>
namespace filament {
namespace camutils {
using namespace filament::math;
template<typename FLOAT>
class FreeFlightManipulator : public Manipulator<FLOAT> {
public:
using vec2 = filament::math::vec2<FLOAT>;
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
using Bookmark = filament::camutils::Bookmark<FLOAT>;
using Base = Manipulator<FLOAT>;
using Config = typename Base::Config;
FreeFlightManipulator(Mode mode, const Config& props) : Base(mode, props) {
setProperties(props);
Base::mEye = Base::mProps.flightStartPosition;
const auto pitch = Base::mProps.flightStartPitch;
const auto yaw = Base::mProps.flightStartYaw;
mTargetEuler = {pitch, yaw};
updateTarget(pitch, yaw);
}
void setProperties(const Config& props) override {
Config resolved = props;
if (resolved.flightPanSpeed == vec2(0, 0)) {
resolved.flightPanSpeed = vec2(0.01, 0.01);
}
if (resolved.flightMaxSpeed == 0.0) {
resolved.flightMaxSpeed = 10.0;
}
if (resolved.flightSpeedSteps == 0) {
resolved.flightSpeedSteps = 80;
}
Base::setProperties(resolved);
}
void updateTarget(FLOAT pitch, FLOAT yaw) {
Base::mTarget = Base::mEye + (mat3::eulerZYX(0, yaw, pitch) * vec3(0.0, 0.0, -1.0));
}
void grabBegin(int x, int y, bool strafe) override {
mGrabWin = {x, y};
mGrabbing = true;
mGrabEuler = mTargetEuler;
}
void grabUpdate(int x, int y) override {
if (!mGrabbing) {
return;
}
const vec2 del = mGrabWin - vec2{x, y};
const auto& grabPitch = mGrabEuler.x;
const auto& grabYaw = mGrabEuler.y;
auto& pitch = mTargetEuler.x;
auto& yaw = mTargetEuler.y;
constexpr double EPSILON = 0.001;
auto panSpeed = Base::mProps.flightPanSpeed;
constexpr FLOAT minPitch = (-F_PI_2 + EPSILON);
constexpr FLOAT maxPitch = ( F_PI_2 - EPSILON);
pitch = clamp(grabPitch + del.y * -panSpeed.y, minPitch, maxPitch);
yaw = fmod(grabYaw + del.x * panSpeed.x, 2.0 * F_PI);
updateTarget(pitch, yaw);
}
void grabEnd() override {
mGrabbing = false;
}
void keyDown(typename Base::Key key) override {
mKeyDown[(int) key] = true;
}
void keyUp(typename Base::Key key) override {
mKeyDown[(int) key] = false;
}
void scroll(int x, int y, FLOAT scrolldelta) override {
const FLOAT halfSpeedSteps = Base::mProps.flightSpeedSteps / 2;
mScrollWheel = clamp(mScrollWheel + scrolldelta, -halfSpeedSteps, halfSpeedSteps);
// Normalize the scroll position from -1 to 1 and calculate the move speed, in world
// units per second.
mScrollPositionNormalized = (mScrollWheel + halfSpeedSteps) / halfSpeedSteps - 1.0;
mMoveSpeed = pow(Base::mProps.flightMaxSpeed, mScrollPositionNormalized);
}
void update(FLOAT deltaTime) override {
vec3 forceLocal { 0.0, 0.0, 0.0 };
if (mKeyDown[(int) Base::Key::FORWARD]) {
forceLocal += vec3{ 0.0, 0.0, -1.0 };
}
if (mKeyDown[(int) Base::Key::LEFT]) {
forceLocal += vec3{ -1.0, 0.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::BACKWARD]) {
forceLocal += vec3{ 0.0, 0.0, 1.0 };
}
if (mKeyDown[(int) Base::Key::RIGHT]) {
forceLocal += vec3{ 1.0, 0.0, 0.0 };
}
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz;
if (mKeyDown[(int) Base::Key::UP]) {
forceWorld += vec3{ 0.0, 1.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::DOWN]) {
forceWorld += vec3{ 0.0, -1.0, 0.0 };
}
forceWorld *= mMoveSpeed;
const auto dampingFactor = Base::mProps.flightMoveDamping;
if (dampingFactor == 0.0) {
// Without damping, we simply treat the force as our velocity.
mEyeVelocity = forceWorld;
} else {
// The dampingFactor acts as "friction", which acts upon the camera in the direction
// opposite its velocity.
// Force is also multiplied by the dampingFactor, to "make up" for the friction.
// This ensures that the max velocity still approaches mMoveSpeed;
vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor;
mEyeVelocity += velocityDelta * deltaTime;
}
const vec3 positionDelta = mEyeVelocity * deltaTime;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
}
Bookmark getCurrentBookmark() const override {
Bookmark bookmark;
bookmark.flight.position = Base::mEye;
bookmark.flight.pitch = mTargetEuler.x;
bookmark.flight.yaw = mTargetEuler.y;
return bookmark;
}
Bookmark getHomeBookmark() const override {
Bookmark bookmark;
bookmark.flight.position = Base::mProps.flightStartPosition;;
bookmark.flight.pitch = Base::mProps.flightStartPitch;
bookmark.flight.yaw = Base::mProps.flightStartYaw;
return bookmark;
}
void jumpToBookmark(const Bookmark& bookmark) override {
Base::mEye = bookmark.flight.position;
updateTarget(bookmark.flight.pitch, bookmark.flight.yaw);
}
private:
vec2 mGrabWin;
vec2 mTargetEuler; // (pitch, yaw)
vec2 mGrabEuler; // (pitch, yaw)
bool mKeyDown[(int) Base::Key::COUNT] = {false};
bool mGrabbing = false;
FLOAT mScrollWheel = 0.0f;
FLOAT mScrollPositionNormalized = 0.0f;
FLOAT mMoveSpeed = 1.0f;
vec3 mEyeVelocity;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_FREEFLIGHT_MANIPULATOR_H */

View File

@@ -0,0 +1,323 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#include "FreeFlightManipulator.h"
#include "MapManipulator.h"
#include "OrbitManipulator.h"
using namespace filament::math;
namespace filament {
namespace camutils {
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::viewport(int width, int height) {
details.viewport[0] = width;
details.viewport[1] = height;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::targetPosition(FLOAT x, FLOAT y, FLOAT z) {
details.targetPosition = {x, y, z};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::upVector(FLOAT x, FLOAT y, FLOAT z) {
details.upVector = {x, y, z};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::zoomSpeed(FLOAT val) {
details.zoomSpeed = val;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitHomePosition(FLOAT x, FLOAT y, FLOAT z) {
details.orbitHomePosition = {x, y, z};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::orbitSpeed(FLOAT x, FLOAT y) {
details.orbitSpeed = {x, y};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDirection(Fov fov) {
details.fovDirection = fov;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::fovDegrees(FLOAT degrees) {
details.fovDegrees = degrees;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::farPlane(FLOAT distance) {
details.farPlane = distance;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapExtent(FLOAT worldWidth, FLOAT worldHeight) {
details.mapExtent = {worldWidth, worldHeight};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::mapMinDistance(FLOAT mindist) {
details.mapMinDistance = mindist;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartPosition(FLOAT x, FLOAT y, FLOAT z) {
details.flightStartPosition = {x, y, z};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightStartOrientation(FLOAT pitch, FLOAT yaw) {
details.flightStartPitch = pitch;
details.flightStartYaw = yaw;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMaxMoveSpeed(FLOAT maxSpeed) {
details.flightMaxSpeed = maxSpeed;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightSpeedSteps(int steps) {
details.flightSpeedSteps = steps;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightPanSpeed(FLOAT x, FLOAT y) {
details.flightPanSpeed = {x, y};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::flightMoveDamping(FLOAT damping) {
details.flightMoveDamping = damping;
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d) {
details.groundPlane = {a, b, c, d};
return *this;
}
template <typename FLOAT> typename
Manipulator<FLOAT>::Builder& Manipulator<FLOAT>::Builder::raycastCallback(RayCallback cb, void* userdata) {
details.raycastCallback = cb;
details.raycastUserdata = userdata;
return *this;
}
template <typename FLOAT>
Manipulator<FLOAT>* Manipulator<FLOAT>::Builder::build(Mode mode) {
switch (mode) {
case Mode::FREE_FLIGHT:
return new FreeFlightManipulator<FLOAT>(mode, details);
case Mode::MAP:
return new MapManipulator<FLOAT>(mode, details);
case Mode::ORBIT:
return new OrbitManipulator<FLOAT>(mode, details);
}
}
template <typename FLOAT>
Manipulator<FLOAT>::Manipulator(Mode mode, const Config& props) : mMode(mode) {
setProperties(props);
}
template <typename FLOAT>
void Manipulator<FLOAT>::setProperties(const Config& props) {
mProps = props;
if (mProps.zoomSpeed == FLOAT(0)) {
mProps.zoomSpeed = 0.01;
}
if (mProps.upVector == vec3(0)) {
mProps.upVector = vec3(0, 1, 0);
}
if (mProps.fovDegrees == FLOAT(0)) {
mProps.fovDegrees = 33;
}
if (mProps.farPlane == FLOAT(0)) {
mProps.farPlane = 5000;
}
if (mProps.mapExtent == vec2(0)) {
mProps.mapExtent = vec2(512);
}
}
template <typename FLOAT>
void Manipulator<FLOAT>::setViewport(int width, int height) {
Config props = mProps;
props.viewport[0] = width;
props.viewport[1] = height;
setProperties(props);
}
template <typename FLOAT>
void Manipulator<FLOAT>::getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const {
*targetPosition = mTarget;
*eyePosition = mEye;
const vec3 gaze = normalize(mTarget - mEye);
const vec3 right = cross(gaze, mProps.upVector);
*upward = cross(right, gaze);
}
template<typename FLOAT>
static bool raycastPlane(const filament::math::vec3<FLOAT>& origin,
const filament::math::vec3<FLOAT>& dir, FLOAT* t, void* userdata) {
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
auto props = (const typename Manipulator<FLOAT>::Config*) userdata;
const vec4 plane = props->groundPlane;
const vec3 n = vec3(plane[0], plane[1], plane[2]);
const vec3 p0 = n * plane[3];
const FLOAT denom = -dot(n, dir);
if (denom > 1e-6) {
const vec3 p0l0 = p0 - origin;
*t = dot(p0l0, n) / -denom;
return *t >= 0;
}
return false;
}
template <typename FLOAT>
void Manipulator<FLOAT>::getRay(int x, int y, vec3* porigin, vec3* pdir) const {
const vec3 gaze = normalize(mTarget - mEye);
const vec3 right = normalize(cross(gaze, mProps.upVector));
const vec3 upward = cross(right, gaze);
const FLOAT width = mProps.viewport[0];
const FLOAT height = mProps.viewport[1];
const FLOAT fov = mProps.fovDegrees * F_PI / 180.0;
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
const FLOAT tangent = tan(fov / 2.0);
const FLOAT aspect = width / height;
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
vec3 dir = gaze;
if (mProps.fovDirection == Fov::VERTICAL) {
dir += right * tangent * u * aspect;
dir += upward * tangent * v;
} else {
dir += right * tangent * u;
dir += upward * tangent * v / aspect;
}
dir = normalize(dir);
*porigin = mEye;
*pdir = dir;
}
template <typename FLOAT>
bool Manipulator<FLOAT>::raycast(int x, int y, vec3* result) const {
vec3 origin, dir;
getRay(x, y, &origin, &dir);
// Choose either the user's callback function or the plane intersector.
auto callback = mProps.raycastCallback;
auto fallback = raycastPlane<FLOAT>;
void* userdata = mProps.raycastUserdata;
if (!callback) {
callback = fallback;
userdata = (void*) &mProps;
}
// If the ray misses, then try the fallback function.
FLOAT t;
if (!callback(mEye, dir, &t, userdata)) {
if (callback == fallback || !fallback(mEye, dir, &t, (void*) &mProps)) {
return false;
}
}
*result = mEye + dir * t;
return true;
}
template <typename FLOAT>
filament::math::vec3<FLOAT> Manipulator<FLOAT>::raycastFarPlane(int x, int y) const {
const filament::math::vec3<FLOAT> gaze = normalize(mTarget - mEye);
const vec3 right = cross(gaze, mProps.upVector);
const vec3 upward = cross(right, gaze);
const FLOAT width = mProps.viewport[0];
const FLOAT height = mProps.viewport[1];
const FLOAT fov = mProps.fovDegrees * math::F_PI / 180.0;
// Remap the grid coordinate into [-1, +1] and shift it to the pixel center.
const FLOAT u = 2.0 * (0.5 + x) / width - 1.0;
const FLOAT v = 2.0 * (0.5 + y) / height - 1.0;
// Compute the tangent of the field-of-view angle as well as the aspect ratio.
const FLOAT tangent = tan(fov / 2.0);
const FLOAT aspect = width / height;
// Adjust the gaze so it goes through the pixel of interest rather than the grid center.
vec3 dir = gaze;
if (mProps.fovDirection == Fov::VERTICAL) {
dir += right * tangent * u * aspect;
dir += upward * tangent * v;
} else {
dir += right * tangent * u;
dir += upward * tangent * v / aspect;
}
return mEye + dir * mProps.farPlane;
}
template <typename FLOAT>
void Manipulator<FLOAT>::keyDown(Manipulator<FLOAT>::Key key) { }
template <typename FLOAT>
void Manipulator<FLOAT>::keyUp(Manipulator<FLOAT>::Key key) { }
template <typename FLOAT>
void Manipulator<FLOAT>::update(FLOAT deltaTime) { }
template class Manipulator<float>;
} // namespace camutils
} // namespace filament

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_MAP_MANIPULATOR_H
#define CAMUTILS_MAP_MANIPULATOR_H
#include <camutils/Manipulator.h>
#include <math/vec3.h>
namespace filament {
namespace camutils {
template<typename FLOAT>
class MapManipulator : public Manipulator<FLOAT> {
public:
using vec2 = math::vec2<FLOAT>;
using vec3 = math::vec3<FLOAT>;
using vec4 = math::vec4<FLOAT>;
using Bookmark = filament::camutils::Bookmark<FLOAT>;
using Base = Manipulator<FLOAT>;
using Config = typename Manipulator<FLOAT>::Config;
MapManipulator(Mode mode, const Config& props) : Manipulator<FLOAT>(mode, props) {
const FLOAT width = Base::mProps.mapExtent.x;
const FLOAT height = Base::mProps.mapExtent.y;
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const FLOAT halfExtent = (horiz ? width : height) / 2.0;
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT distance = halfExtent / tan(fov / 2.0);
Base::mTarget = Base::mProps.targetPosition;
Base::mEye = Base::mTarget + distance * targetToEye;
}
void grabBegin(int x, int y, bool strafe) override {
if (strafe || !Base::raycast(x, y, &mGrabScene)) {
return;
}
mGrabFar = Base::raycastFarPlane(x, y);
mGrabEye = Base::mEye;
mGrabTarget = Base::mTarget;
mGrabbing = true;
}
void grabUpdate(int x, int y) override {
if (mGrabbing) {
const FLOAT ulen = distance(mGrabScene, mGrabEye);
const FLOAT vlen = distance(mGrabFar, mGrabScene);
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
const vec3 eyePosition = mGrabEye + translation;
const vec3 targetPosition = mGrabTarget + translation;
moveWithConstraints(eyePosition, targetPosition);
}
}
void grabEnd() override {
mGrabbing = false;
}
void scroll(int x, int y, FLOAT scrolldelta) override {
vec3 grabScene;
if (!Base::raycast(x, y, &grabScene)) {
return;
}
// Find the direction of travel for the dolly. We do not normalize since it
// is desirable to move faster when further away from the targetPosition.
vec3 u = grabScene - Base::mEye;
// Prevent getting stuck when zooming in.
if (scrolldelta < 0) {
const FLOAT distanceToSurface = length(u);
if (distanceToSurface < Base::mProps.zoomSpeed) {
return;
}
}
u *= -scrolldelta * Base::mProps.zoomSpeed;
const vec3 eyePosition = Base::mEye + u;
const vec3 targetPosition = Base::mTarget + u;
moveWithConstraints(eyePosition, targetPosition);
}
Bookmark getCurrentBookmark() const override {
const vec3 dir = normalize(Base::mTarget - Base::mEye);
FLOAT distance;
raycastPlane(Base::mEye, dir, &distance);
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT halfExtent = distance * tan(fov / 2.0);
vec3 targetPosition = Base::mEye + dir * distance;
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
const vec3 vvec = cross(targetToEye, uvec);
const vec3 centerToTarget = targetPosition - Base::mProps.targetPosition;
Bookmark bookmark;
bookmark.mode = Mode::MAP;
bookmark.map.extent = halfExtent * 2.0;
bookmark.map.center.x = dot(uvec, centerToTarget);
bookmark.map.center.y = dot(vvec, centerToTarget);
bookmark.orbit.theta = 0;
bookmark.orbit.phi = 0;
bookmark.orbit.pivot = Base::mProps.targetPosition +
uvec * bookmark.map.center.x +
vvec * bookmark.map.center.y;
bookmark.orbit.distance = halfExtent / tan(fov / 2.0);
return bookmark;
}
Bookmark getHomeBookmark() const override {
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT width = Base::mProps.mapExtent.x;
const FLOAT height = Base::mProps.mapExtent.y;
const bool horiz = Base::mProps.fovDirection == Fov::HORIZONTAL;
Bookmark bookmark;
bookmark.mode = Mode::MAP;
bookmark.map.extent = horiz ? width : height;
bookmark.map.center.x = 0;
bookmark.map.center.y = 0;
bookmark.orbit.theta = 0;
bookmark.orbit.phi = 0;
bookmark.orbit.pivot = Base::mTarget;
bookmark.orbit.distance = 0.5 * bookmark.map.extent / tan(fov / 2.0);
// TODO: Add optional boundary constraints here.
return bookmark;
}
void jumpToBookmark(const Bookmark& bookmark) override {
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const FLOAT halfExtent = bookmark.map.extent / 2.0;
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT distance = halfExtent / tan(fov / 2.0);
vec3 uvec = cross(Base::mProps.upVector, targetToEye);
vec3 vvec = cross(targetToEye, uvec);
uvec = normalize(uvec) * bookmark.map.center.x;
vvec = normalize(vvec) * bookmark.map.center.y;
Base::mTarget = Base::mProps.targetPosition + uvec + vvec;
Base::mEye = Base::mTarget + distance * targetToEye;
}
private:
bool raycastPlane(const vec3& origin, const vec3& dir, FLOAT* t) const {
const vec4 plane = Base::mProps.groundPlane;
const vec3 n = vec3(plane[0], plane[1], plane[2]);
const vec3 p0 = n * plane[3];
const FLOAT denom = -dot(n, dir);
if (denom > 1e-6) {
const vec3 p0l0 = p0 - origin;
*t = dot(p0l0, n) / -denom;
return *t >= 0;
}
return false;
}
void moveWithConstraints(vec3 eye, vec3 targetPosition) {
Base::mEye = eye;
Base::mTarget = targetPosition;
// TODO: Add optional boundary constraints here.
}
private:
bool mGrabbing = false;
vec3 mGrabScene;
vec3 mGrabFar;
vec3 mGrabEye;
vec3 mGrabTarget;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_MAP_MANIPULATOR_H */

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_ORBIT_MANIPULATOR_H
#define CAMUTILS_ORBIT_MANIPULATOR_H
#include <camutils/Manipulator.h>
#include <math/scalar.h>
#define MAX_PHI (F_PI / 2.0 - 0.001)
namespace filament {
namespace camutils {
using namespace filament::math;
template<typename FLOAT>
class OrbitManipulator : public Manipulator<FLOAT> {
public:
using vec2 = filament::math::vec2<FLOAT>;
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
using Bookmark = filament::camutils::Bookmark<FLOAT>;
using Base = Manipulator<FLOAT>;
using Config = typename Base::Config;
enum GrabState { INACTIVE, ORBITING, PANNING };
OrbitManipulator(Mode mode, const Config& props) : Base(mode, props) {
setProperties(props);
Base::mEye = Base::mProps.orbitHomePosition;
mPivot = Base::mTarget = Base::mProps.targetPosition;
}
void setProperties(const Config& props) override {
Config resolved = props;
if (resolved.orbitHomePosition == vec3(0)) {
resolved.orbitHomePosition = vec3(0, 0, 1);
}
if (resolved.orbitSpeed == vec2(0)) {
resolved.orbitSpeed = vec2(0.01);
}
// By default, place the ground plane so that it aligns with the targetPosition position.
// This is used only when PANNING.
if (resolved.groundPlane == vec4(0)) {
const FLOAT d = length(resolved.targetPosition);
const vec3 n = normalize(resolved.orbitHomePosition - resolved.targetPosition);
resolved.groundPlane = vec4(n, -d);
}
Base::setProperties(resolved);
}
void grabBegin(int x, int y, bool strafe) override {
mGrabState = strafe ? PANNING : ORBITING;
mGrabPivot = mPivot;
mGrabEye = Base::mEye;
mGrabTarget = Base::mTarget;
mGrabBookmark = getCurrentBookmark();
mGrabWinX = x;
mGrabWinY = y;
mGrabFar = Base::raycastFarPlane(x, y);
Base::raycast(x, y, &mGrabScene);
}
void grabUpdate(int x, int y) override {
const int delx = mGrabWinX - x;
const int dely = mGrabWinY - y;
if (mGrabState == ORBITING) {
Bookmark bookmark = getCurrentBookmark();
const FLOAT theta = delx * Base::mProps.orbitSpeed.x;
const FLOAT phi = dely * Base::mProps.orbitSpeed.y;
const FLOAT maxPhi = MAX_PHI;
bookmark.orbit.phi = clamp(mGrabBookmark.orbit.phi + phi, -maxPhi, +maxPhi);
bookmark.orbit.theta = mGrabBookmark.orbit.theta + theta;
jumpToBookmark(bookmark);
}
if (mGrabState == PANNING) {
const FLOAT ulen = distance(mGrabScene, mGrabEye);
const FLOAT vlen = distance(mGrabFar, mGrabScene);
const vec3 translation = (mGrabFar - Base::raycastFarPlane(x, y)) * ulen / vlen;
mPivot = mGrabPivot + translation;
Base::mEye = mGrabEye + translation;
Base::mTarget = mGrabTarget + translation;
}
}
void grabEnd() override {
mGrabState = INACTIVE;
}
void scroll(int x, int y, FLOAT scrolldelta) override {
const vec3 gaze = normalize(Base::mTarget - Base::mEye);
const vec3 movement = gaze * Base::mProps.zoomSpeed * -scrolldelta;
const vec3 v0 = mPivot - Base::mEye;
Base::mEye += movement;
Base::mTarget += movement;
const vec3 v1 = mPivot - Base::mEye;
// Check if the camera has moved past the point of interest.
if (dot(v0, v1) < 0) {
mFlipped = !mFlipped;
}
}
Bookmark getCurrentBookmark() const override {
Bookmark bookmark;
bookmark.mode = Mode::ORBIT;
const vec3 pivotToEye = Base::mEye - mPivot;
const FLOAT d = length(pivotToEye);
const FLOAT x = pivotToEye.x / d;
const FLOAT y = pivotToEye.y / d;
const FLOAT z = pivotToEye.z / d;
bookmark.orbit.phi = asin(y);
bookmark.orbit.theta = atan2(x, z);
bookmark.orbit.distance = mFlipped ? -d : d;
bookmark.orbit.pivot = mPivot;
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT halfExtent = d * tan(fov / 2.0);
const vec3 targetToEye = Base::mProps.groundPlane.xyz;
const vec3 uvec = cross(Base::mProps.upVector, targetToEye);
const vec3 vvec = cross(targetToEye, uvec);
const vec3 centerToTarget = mPivot - Base::mProps.targetPosition;
bookmark.map.extent = halfExtent * 2;
bookmark.map.center.x = dot(uvec, centerToTarget);
bookmark.map.center.y = dot(vvec, centerToTarget);
return bookmark;
}
Bookmark getHomeBookmark() const override {
Bookmark bookmark;
bookmark.mode = Mode::ORBIT;
bookmark.orbit.phi = FLOAT(0);
bookmark.orbit.theta = FLOAT(0);
bookmark.orbit.pivot = Base::mProps.targetPosition;
bookmark.orbit.distance = distance(Base::mProps.targetPosition, Base::mProps.orbitHomePosition);
const FLOAT fov = Base::mProps.fovDegrees * math::F_PI / 180.0;
const FLOAT halfExtent = bookmark.orbit.distance * tan(fov / 2.0);
bookmark.map.extent = halfExtent * 2;
bookmark.map.center.x = 0;
bookmark.map.center.y = 0;
return bookmark;
}
void jumpToBookmark(const Bookmark& bookmark) override {
mPivot = bookmark.orbit.pivot;
const FLOAT x = sin(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
const FLOAT y = sin(bookmark.orbit.phi);
const FLOAT z = cos(bookmark.orbit.theta) * cos(bookmark.orbit.phi);
Base::mEye = mPivot + vec3(x, y, z) * abs(bookmark.orbit.distance);
mFlipped = bookmark.orbit.distance < 0;
Base::mTarget = Base::mEye + vec3(x, y, z) * (mFlipped ? 1.0 : -1.0);
}
private:
GrabState mGrabState = INACTIVE;
bool mFlipped = false;
vec3 mGrabPivot;
vec3 mGrabScene;
vec3 mGrabFar;
vec3 mGrabEye;
vec3 mGrabTarget;
Bookmark mGrabBookmark;
int mGrabWinX;
int mGrabWinY;
vec3 mPivot;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_ORBIT_MANIPULATOR_H */

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <camutils/Bookmark.h>
#include <camutils/Manipulator.h>
#include <gtest/gtest.h>
using namespace filament::math;
namespace camutils = filament::camutils;
using CamManipulator = camutils::Manipulator<float>;
class CamUtilsTest : public testing::Test {};
#define EXPECT_VEC_EQ(a, x, y, z) expectVecEq(a, {x, y, z}, __LINE__)
static void expectVecEq(float3 a, float3 b, int line) {
EXPECT_FLOAT_EQ(a.x, b.x);
EXPECT_FLOAT_EQ(a.y, b.y);
EXPECT_FLOAT_EQ(a.z, b.z);
}
TEST_F(CamUtilsTest, Orbit) {
float3 eye, targetPosition, up;
CamManipulator* orbit = CamManipulator::Builder()
.viewport(256, 256)
.targetPosition(0, 0, 0)
.upVector(0, 1, 0)
.zoomSpeed(0.01)
.orbitHomePosition(0, 0, 4)
.orbitSpeed(1, 1)
.build(camutils::Mode::ORBIT);
orbit->getLookAt(&eye, &targetPosition, &up);
EXPECT_VEC_EQ(eye, 0, 0, 4);
EXPECT_VEC_EQ(targetPosition, 0, 0, 0);
EXPECT_VEC_EQ(up, 0, 1, 0);
orbit->grabBegin(100, 100, false);
orbit->grabUpdate(200, 100);
orbit->grabEnd();
orbit->getLookAt(&eye, &targetPosition, &up);
EXPECT_VEC_EQ(eye, 2.0254626, 0, 3.4492755);
EXPECT_VEC_EQ(targetPosition, 1.519097, 0, 2.5869565);
EXPECT_VEC_EQ(up, 0, 1, 0);
delete orbit;
}
TEST_F(CamUtilsTest, Map) {
float3 eye, targetPosition, up;
CamManipulator* map = CamManipulator::Builder()
.viewport(256, 256)
.targetPosition(0, 0, 0)
.zoomSpeed(0.01)
.orbitHomePosition(0, 0, 4)
.build(camutils::Mode::MAP);
delete map;
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,307 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ARRAY_GROWTH_POLICY_H
#define TSL_ARRAY_GROWTH_POLICY_H
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstddef>
#include <iterator>
#include <limits>
#include <ratio>
#include <stdexcept>
#ifdef __EXCEPTIONS
# define THROW(_e, _m) throw _e(_m)
#else
# include <stdio.h>
# ifndef NDEBUG
# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0)
# else
# define THROW(_e, _m) std::terminate()
# endif
#endif
namespace tsl {
namespace ah {
/**
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
*
* GrowthFactor must be a power of two >= 2.
*/
template<std::size_t GrowthFactor>
class power_of_two_growth_policy {
public:
/**
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
*
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
* bucket_for_hash must always return 0 in this case.
*/
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
if(min_bucket_count_in_out > 0) {
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
m_mask = min_bucket_count_in_out - 1;
}
else {
m_mask = 0;
}
}
/**
* Return the bucket [0, bucket_count()) to which the hash belongs.
* If bucket_count() is 0, it must always return 0.
*/
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash & m_mask;
}
/**
* Return the number of buckets that should be used on next growth.
*/
std::size_t next_bucket_count() const {
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
return (m_mask + 1) * GrowthFactor;
}
/**
* Return the maximum number of buckets supported by the policy.
*/
std::size_t max_bucket_count() const {
// Largest power of two.
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
}
/**
* Reset the growth policy as if it was created with a bucket count of 0.
* After a clear, the policy must always return 0 when bucket_for_hash is called.
*/
void clear() noexcept {
m_mask = 0;
}
private:
static std::size_t round_up_to_power_of_two(std::size_t value) {
if(is_power_of_two(value)) {
return value;
}
if(value == 0) {
return 1;
}
--value;
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
value |= value >> i;
}
return value + 1;
}
static constexpr bool is_power_of_two(std::size_t value) {
return value != 0 && (value & (value - 1)) == 0;
}
protected:
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
std::size_t m_mask;
};
/**
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
* to a bucket. Slower but it can be useful if you want a slower growth.
*/
template<class GrowthFactor = std::ratio<3, 2>>
class mod_growth_policy {
public:
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
if(min_bucket_count_in_out > 0) {
m_mod = min_bucket_count_in_out;
}
else {
m_mod = 1;
}
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash % m_mod;
}
std::size_t next_bucket_count() const {
if(m_mod == max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
if(!std::isnormal(next_bucket_count)) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
if(next_bucket_count > double(max_bucket_count())) {
return max_bucket_count();
}
else {
return std::size_t(next_bucket_count);
}
}
std::size_t max_bucket_count() const {
return MAX_BUCKET_COUNT;
}
void clear() noexcept {
m_mod = 1;
}
private:
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
static const std::size_t MAX_BUCKET_COUNT =
std::size_t(double(
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
));
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
std::size_t m_mod;
};
namespace detail {
static constexpr const std::array<std::size_t, 40> PRIMES = {{
1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul,
1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul,
402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul
}};
template<unsigned int IPrime>
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
// compiler can optimize the modulo code better with a constant known at the compilation.
static constexpr const std::array<std::size_t(*)(std::size_t), 40> MOD_PRIME = {{
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>
}};
}
/**
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::ah::power_of_two_growth_policy in
* general but will probably distribute the values around better in the buckets with a poor hash function.
*
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
*
* With a switch the code would look like:
* \code
* switch(iprime) { // iprime is the current prime of the hash table
* case 0: hash % 5ul;
* break;
* case 1: hash % 17ul;
* break;
* case 2: hash % 29ul;
* break;
* ...
* }
* \endcode
*
* Due to the constant variable in the modulo the compiler is able to optimize the operation
* by a series of multiplications, substractions and shifts.
*
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment.
*/
class prime_growth_policy {
public:
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
detail::PRIMES.end(), min_bucket_count_in_out);
if(it_prime == detail::PRIMES.end()) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
if(min_bucket_count_in_out > 0) {
min_bucket_count_in_out = *it_prime;
}
else {
min_bucket_count_in_out = 0;
}
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return detail::MOD_PRIME[m_iprime](hash);
}
std::size_t next_bucket_count() const {
if(m_iprime + 1 >= detail::PRIMES.size()) {
THROW(std::length_error, "The hash table exceeds its maximum size.");
}
return detail::PRIMES[m_iprime + 1];
}
std::size_t max_bucket_count() const {
return detail::PRIMES.back();
}
void clear() noexcept {
m_iprime = 0;
}
private:
unsigned int m_iprime;
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
"The type of m_iprime is not big enough.");
};
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,863 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ARRAY_MAP_H
#define TSL_ARRAY_MAP_H
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include "array_hash.h"
namespace tsl {
/**
* Implementation of a cache-conscious string hash map.
*
* The map stores the strings as `const CharT*`. If `StoreNullTerminator` is true,
* the strings are stored with the a null-terminator (the `key()` method of the iterators
* will return a pointer to this null-terminated string). Otherwise the null character
* is not stored (which allow an economy of 1 byte per string).
*
* The value `T` must be either nothrow move-constructible, copy-constructible or both.
*
* The size of a key string is limited to `std::numeric_limits<KeySizeT>::max() - 1`.
* That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter.
* See `max_key_size()` for an easy access to this limit.
*
* The number of elements in the map is limited to `std::numeric_limits<IndexSizeT>::max()`.
* That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter.
* See `max_size()` for an easy access to this limit.
*
* Iterators invalidation:
* - clear, operator=: always invalidate the iterators.
* - insert, emplace, operator[]: always invalidate the iterators.
* - erase: always invalidate the iterators.
* - shrink_to_fit: always invalidate the iterators.
*/
template<class CharT,
class T,
class Hash = tsl::ah::str_hash<CharT>,
class KeyEqual = tsl::ah::str_equal<CharT>,
bool StoreNullTerminator = true,
class KeySizeT = std::uint16_t,
class IndexSizeT = std::uint32_t,
class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>>
class array_map {
private:
template<typename U>
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
using ht = tsl::detail_array_hash::array_hash<CharT, T, Hash, KeyEqual, StoreNullTerminator,
KeySizeT, IndexSizeT, GrowthPolicy>;
public:
using char_type = typename ht::char_type;
using mapped_type = T;
using key_size_type = typename ht::key_size_type;
using index_size_type = typename ht::index_size_type;
using size_type = typename ht::size_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
public:
array_map(): array_map(ht::DEFAULT_INIT_BUCKET_COUNT) {
}
explicit array_map(size_type bucket_count,
const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR)
{
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
array_map(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
const Hash& hash = Hash()): array_map(bucket_count, hash)
{
insert(first, last);
}
#ifdef TSL_AH_HAS_STRING_VIEW
array_map(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
const Hash& hash = Hash()): array_map(bucket_count, hash)
{
insert(init);
}
#else
array_map(std::initializer_list<std::pair<const CharT*, T>> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
const Hash& hash = Hash()): array_map(bucket_count, hash)
{
insert(init);
}
#endif
#ifdef TSL_AH_HAS_STRING_VIEW
array_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
clear();
reserve(ilist.size());
insert(ilist);
return *this;
}
#else
array_map& operator=(std::initializer_list<std::pair<const CharT*, T>> ilist) {
clear();
reserve(ilist.size());
insert(ilist);
return *this;
}
#endif
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
#ifdef TSL_AH_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
return m_ht.emplace(key.data(), key.size(), value);
}
#else
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
return m_ht.emplace(key, std::char_traits<CharT>::length(key), value);
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
return m_ht.emplace(key.data(), key.size(), value);
}
#endif
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, const T& value) {
return m_ht.emplace(key, key_size, value);
}
#ifdef TSL_AH_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
return m_ht.emplace(key.data(), key.size(), std::move(value));
}
#else
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::move(value));
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
return m_ht.emplace(key.data(), key.size(), std::move(value));
}
#endif
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
return m_ht.emplace(key, key_size, std::move(value));
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
void insert(InputIt first, InputIt last) {
if(std::is_base_of<std::forward_iterator_tag,
typename std::iterator_traits<InputIt>::iterator_category>::value)
{
const auto nb_elements_insert = std::distance(first, last);
const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size();
if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
reserve(size() + std::size_t(nb_elements_insert));
}
}
for(auto it = first; it != last; ++it) {
insert_pair(*it);
}
}
#ifdef TSL_AH_HAS_STRING_VIEW
void insert(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
insert(ilist.begin(), ilist.end());
}
#else
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
insert(ilist.begin(), ilist.end());
}
#endif
#ifdef TSL_AH_HAS_STRING_VIEW
template<class M>
std::pair<iterator, bool> insert_or_assign(const std::basic_string_view<CharT>& key, M&& obj) {
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
}
#else
template<class M>
std::pair<iterator, bool> insert_or_assign(const CharT* key, M&& obj) {
return m_ht.insert_or_assign(key, std::char_traits<CharT>::length(key), std::forward<M>(obj));
}
template<class M>
std::pair<iterator, bool> insert_or_assign(const std::basic_string<CharT>& key, M&& obj) {
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
}
#endif
template<class M>
std::pair<iterator, bool> insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) {
return m_ht.insert_or_assign(key, key_size, std::forward<M>(obj));
}
#ifdef TSL_AH_HAS_STRING_VIEW
template<class... Args>
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
}
#else
template<class... Args>
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
}
#endif
template<class... Args>
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
return m_ht.emplace(key, key_size, std::forward<Args>(args)...);
}
/**
* Erase has an amortized O(1) runtime complexity, but even if it removes the key immediately,
* it doesn't do the same for the associated value T.
*
* T will only be removed when the ratio between the size of the map and
* the size of the map + the number of deleted values still stored is low enough.
*
* To force the deletion you can call shrink_to_fit.
*/
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
/**
* @copydoc erase(const_iterator pos)
*/
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc erase(const_iterator pos)
*/
size_type erase(const std::basic_string_view<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#else
/**
* @copydoc erase(const_iterator pos)
*/
size_type erase(const CharT* key) {
return m_ht.erase(key, std::char_traits<CharT>::length(key));
}
/**
* @copydoc erase(const_iterator pos)
*/
size_type erase(const std::basic_string<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#endif
/**
* @copydoc erase(const_iterator pos)
*/
size_type erase_ks(const CharT* key, size_type key_size) {
return m_ht.erase(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
size_type erase(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.erase(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
size_type erase(const CharT* key, std::size_t precalculated_hash) {
return m_ht.erase(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
size_type erase(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.erase(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* @copydoc erase(const_iterator pos)
*
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.erase(key, key_size, precalculated_hash);
}
void swap(array_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
#ifdef TSL_AH_HAS_STRING_VIEW
T& at(const std::basic_string_view<CharT>& key) {
return m_ht.at(key.data(), key.size());
}
const T& at(const std::basic_string_view<CharT>& key) const {
return m_ht.at(key.data(), key.size());
}
#else
T& at(const CharT* key) {
return m_ht.at(key, std::char_traits<CharT>::length(key));
}
const T& at(const CharT* key) const {
return m_ht.at(key, std::char_traits<CharT>::length(key));
}
T& at(const std::basic_string<CharT>& key) {
return m_ht.at(key.data(), key.size());
}
const T& at(const std::basic_string<CharT>& key) const {
return m_ht.at(key.data(), key.size());
}
#endif
T& at_ks(const CharT* key, size_type key_size) {
return m_ht.at(key, key_size);
}
const T& at_ks(const CharT* key, size_type key_size) const {
return m_ht.at(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
T& at(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.at(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const T& at(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.at(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
T& at(const CharT* key, std::size_t precalculated_hash) {
return m_ht.at(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const T& at(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.at(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
T& at(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.at(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const T& at(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.at(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.at(key, key_size, precalculated_hash);
}
/**
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.at(key, key_size, precalculated_hash);
}
#ifdef TSL_AH_HAS_STRING_VIEW
T& operator[](const std::basic_string_view<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
#else
T& operator[](const CharT* key) { return m_ht.access_operator(key, std::char_traits<CharT>::length(key)); }
T& operator[](const std::basic_string<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
#endif
#ifdef TSL_AH_HAS_STRING_VIEW
size_type count(const std::basic_string_view<CharT>& key) const {
return m_ht.count(key.data(), key.size());
}
#else
size_type count(const CharT* key) const {
return m_ht.count(key, std::char_traits<CharT>::length(key));
}
size_type count(const std::basic_string<CharT>& key) const {
return m_ht.count(key.data(), key.size());
}
#endif
size_type count_ks(const CharT* key, size_type key_size) const {
return m_ht.count(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
*/
size_type count(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.count(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
*/
size_type count(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.count(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
*/
size_type count(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.count(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.count(key, key_size, precalculated_hash);
}
#ifdef TSL_AH_HAS_STRING_VIEW
iterator find(const std::basic_string_view<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string_view<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#else
iterator find(const CharT* key) {
return m_ht.find(key, std::char_traits<CharT>::length(key));
}
const_iterator find(const CharT* key) const {
return m_ht.find(key, std::char_traits<CharT>::length(key));
}
iterator find(const std::basic_string<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#endif
iterator find_ks(const CharT* key, size_type key_size) {
return m_ht.find(key, key_size);
}
const_iterator find_ks(const CharT* key, size_type key_size) const {
return m_ht.find(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
iterator find(const CharT* key, std::size_t precalculated_hash) {
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.find(key, key_size, precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.find(key, key_size, precalculated_hash);
}
#ifdef TSL_AH_HAS_STRING_VIEW
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#else
std::pair<iterator, iterator> equal_range(const CharT* key) {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
}
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
}
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#endif
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
return m_ht.equal_range(key, key_size);
}
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
return m_ht.equal_range(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.equal_range(key, key_size, precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, key_size, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Return the `const_iterator it` as an `iterator`.
*/
iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); }
/**
* Serialize the map through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the following calls:
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U.
* - `void operator()(const CharT* value, std::size_t value_size);`
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
* in the hands of the `Serializer` function object if compatibility is required.
*/
template<class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized map through the `deserializer` parameter.
*
* The `deserializer` parameter must be a function object that supports the following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U.
* - `void operator()(CharT* value_out, std::size_t value_size);`
*
* If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
* KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the
* serialized map. Otherwise the behaviour is undefined with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `CharT` and `T` of the `array_map` are not the same as the
* types used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
*/
template<class Deserializer>
static array_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
array_map map(0);
map.m_ht.deserialize(deserializer, hash_compatible);
return map;
}
friend bool operator==(const array_map& lhs, const array_map& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size());
if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) {
return false;
}
}
return true;
}
friend bool operator!=(const array_map& lhs, const array_map& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(array_map& lhs, array_map& rhs) {
lhs.swap(rhs);
}
private:
template<class U, class V>
void insert_pair(const std::pair<U, V>& value) {
insert(value.first, value.second);
}
template<class U, class V>
void insert_pair(std::pair<U, V>&& value) {
insert(value.first, std::move(value.second));
}
public:
static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE;
private:
ht m_ht;
};
/**
* Same as
* `tsl::array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
*/
template<class CharT,
class T,
class Hash = tsl::ah::str_hash<CharT>,
class KeyEqual = tsl::ah::str_equal<CharT>,
bool StoreNullTerminator = true,
class KeySizeT = std::uint16_t,
class IndexSizeT = std::uint32_t>
using array_pg_map = array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator,
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
} //end namespace tsl
#endif

View File

@@ -0,0 +1,664 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ARRAY_SET_H
#define TSL_ARRAY_SET_H
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include "array_hash.h"
namespace tsl {
/**
* Implementation of a cache-conscious string hash set.
*
* The set stores the strings as `const CharT*`. If `StoreNullTerminator` is true,
* the strings are stored with the a null-terminator (the `key()` method of the iterators
* will return a pointer to this null-terminated string). Otherwise the null character
* is not stored (which allow an economy of 1 byte per string).
*
* The size of a key string is limited to `std::numeric_limits<KeySizeT>::max() - 1`.
* That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter.
* See `max_key_size()` for an easy access to this limit.
*
* The number of elements in the set is limited to `std::numeric_limits<IndexSizeT>::max()`.
* That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter.
* See `max_size()` for an easy access to this limit.
*
* Iterators invalidation:
* - clear, operator=: always invalidate the iterators.
* - insert, emplace, operator[]: always invalidate the iterators.
* - erase: always invalidate the iterators.
* - shrink_to_fit: always invalidate the iterators.
*/
template<class CharT,
class Hash = tsl::ah::str_hash<CharT>,
class KeyEqual = tsl::ah::str_equal<CharT>,
bool StoreNullTerminator = true,
class KeySizeT = std::uint16_t,
class IndexSizeT = std::uint32_t,
class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>>
class array_set {
private:
template<typename U>
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
using ht = tsl::detail_array_hash::array_hash<CharT, void, Hash, KeyEqual, StoreNullTerminator,
KeySizeT, IndexSizeT, GrowthPolicy>;
public:
using char_type = typename ht::char_type;
using key_size_type = typename ht::key_size_type;
using index_size_type = typename ht::index_size_type;
using size_type = typename ht::size_type;
using hasher = typename ht::hasher;
using key_equal = typename ht::key_equal;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
array_set(): array_set(ht::DEFAULT_INIT_BUCKET_COUNT) {
}
explicit array_set(size_type bucket_count,
const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR)
{
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
array_set(InputIt first, InputIt last,
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
const Hash& hash = Hash()): array_set(bucket_count, hash)
{
insert(first, last);
}
#ifdef TSL_AH_HAS_STRING_VIEW
array_set(std::initializer_list<std::basic_string_view<CharT>> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
const Hash& hash = Hash()): array_set(bucket_count, hash)
{
insert(init);
}
#else
array_set(std::initializer_list<const CharT*> init,
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
const Hash& hash = Hash()): array_set(bucket_count, hash)
{
insert(init);
}
#endif
#ifdef TSL_AH_HAS_STRING_VIEW
array_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
clear();
reserve(ilist.size());
insert(ilist);
return *this;
}
#else
array_set& operator=(std::initializer_list<const CharT*> ilist) {
clear();
reserve(ilist.size());
insert(ilist);
return *this;
}
#endif
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
#ifdef TSL_AH_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
return m_ht.emplace(key.data(), key.size());
}
#else
std::pair<iterator, bool> insert(const CharT* key) {
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
return m_ht.emplace(key.data(), key.size());
}
#endif
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
return m_ht.emplace(key, key_size);
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
void insert(InputIt first, InputIt last) {
if(std::is_base_of<std::forward_iterator_tag,
typename std::iterator_traits<InputIt>::iterator_category>::value)
{
const auto nb_elements_insert = std::distance(first, last);
const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size();
if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
reserve(size() + std::size_t(nb_elements_insert));
}
}
for(auto it = first; it != last; ++it) {
insert(*it);
}
}
#ifdef TSL_AH_HAS_STRING_VIEW
void insert(std::initializer_list<std::basic_string_view<CharT>> ilist) {
insert(ilist.begin(), ilist.end());
}
#else
void insert(std::initializer_list<const CharT*> ilist) {
insert(ilist.begin(), ilist.end());
}
#endif
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc emplace_ks(const CharT* key, size_type key_size)
*/
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
return m_ht.emplace(key.data(), key.size());
}
#else
/**
* @copydoc emplace_ks(const CharT* key, size_type key_size)
*/
std::pair<iterator, bool> emplace(const CharT* key) {
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
}
/**
* @copydoc emplace_ks(const CharT* key, size_type key_size)
*/
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key) {
return m_ht.emplace(key.data(), key.size());
}
#endif
/**
* No difference compared to the insert method. Mainly here for coherence with array_map.
*/
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
return m_ht.emplace(key, key_size);
}
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
#ifdef TSL_AH_HAS_STRING_VIEW
size_type erase(const std::basic_string_view<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#else
size_type erase(const CharT* key) {
return m_ht.erase(key, std::char_traits<CharT>::length(key));
}
size_type erase(const std::basic_string<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#endif
size_type erase_ks(const CharT* key, size_type key_size) {
return m_ht.erase(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
size_type erase(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.erase(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
size_type erase(const CharT* key, std::size_t precalculated_hash) {
return m_ht.erase(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
size_type erase(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.erase(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.erase(key, key_size, precalculated_hash);
}
void swap(array_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
#ifdef TSL_AH_HAS_STRING_VIEW
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#else
size_type count(const CharT* key) const { return m_ht.count(key, std::char_traits<CharT>::length(key)); }
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#endif
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
*/
size_type count(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.count(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
*/
size_type count(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.count(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
*/
size_type count(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.count(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.count(key, key_size, precalculated_hash);
}
#ifdef TSL_AH_HAS_STRING_VIEW
iterator find(const std::basic_string_view<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string_view<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#else
iterator find(const CharT* key) {
return m_ht.find(key, std::char_traits<CharT>::length(key));
}
const_iterator find(const CharT* key) const {
return m_ht.find(key, std::char_traits<CharT>::length(key));
}
iterator find(const std::basic_string<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#endif
iterator find_ks(const CharT* key, size_type key_size) {
return m_ht.find(key, key_size);
}
const_iterator find_ks(const CharT* key, size_type key_size) const {
return m_ht.find(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
iterator find(const CharT* key, std::size_t precalculated_hash) {
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.find(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.find(key, key_size, precalculated_hash);
}
/**
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.find(key, key_size, precalculated_hash);
}
#ifdef TSL_AH_HAS_STRING_VIEW
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#else
std::pair<iterator, iterator> equal_range(const CharT* key) {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
}
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
}
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#endif
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
return m_ht.equal_range(key, key_size);
}
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
return m_ht.equal_range(key, key_size);
}
#ifdef TSL_AH_HAS_STRING_VIEW
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
#else
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
}
#endif
/**
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
*/
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
return m_ht.equal_range(key, key_size, precalculated_hash);
}
/**
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
*/
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
return m_ht.equal_range(key, key_size, precalculated_hash);
}
/*
* Bucket interface
*/
size_type bucket_count() const { return m_ht.bucket_count(); }
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
/*
* Hash policy
*/
float load_factor() const { return m_ht.load_factor(); }
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
void rehash(size_type count) { m_ht.rehash(count); }
void reserve(size_type count) { m_ht.reserve(count); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
key_equal key_eq() const { return m_ht.key_eq(); }
/*
* Other
*/
/**
* Return the `const_iterator it` as an `iterator`.
*/
iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); }
/**
* Serialize the set through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the following calls:
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U.
* - `void operator()(const CharT* value, std::size_t value_size);`
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
* in the hands of the `Serializer` function object if compatibility is required.
*/
template<class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized set through the `deserializer` parameter.
*
* The `deserializer` parameter must be a function object that supports the following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t` and `float` must be supported for U.
* - `void operator()(CharT* value_out, std::size_t value_size);`
*
* If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
* KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the
* serialized set. Otherwise the behaviour is undefined with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `CharT` of the `array_set` is not the same as the
* type used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
*/
template<class Deserializer>
static array_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
array_set set(0);
set.m_ht.deserialize(deserializer, hash_compatible);
return set;
}
friend bool operator==(const array_set& lhs, const array_set& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size());
if(it_element_rhs == rhs.cend()) {
return false;
}
}
return true;
}
friend bool operator!=(const array_set& lhs, const array_set& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(array_set& lhs, array_set& rhs) {
lhs.swap(rhs);
}
public:
static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE;
private:
ht m_ht;
};
/**
* Same as
* `tsl::array_set<CharT, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
*/
template<class CharT,
class Hash = tsl::ah::str_hash<CharT>,
class KeyEqual = tsl::ah::str_equal<CharT>,
bool StoreNullTerminator = true,
class KeySizeT = std::uint16_t,
class IndexSizeT = std::uint32_t>
using array_pg_set = array_set<CharT, Hash, KeyEqual, StoreNullTerminator,
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
} //end namespace tsl
#endif

2090
ios/include/tsl/htrie_hash.h Normal file

File diff suppressed because it is too large Load Diff

647
ios/include/tsl/htrie_map.h Normal file
View File

@@ -0,0 +1,647 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_HTRIE_MAP_H
#define TSL_HTRIE_MAP_H
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <string>
#include <utility>
#include "htrie_hash.h"
namespace tsl {
/**
* Implementation of a hat-trie map.
*
* The value T must be either nothrow move-constructible/assignable, copy-constructible or both.
*
* The size of a key string is limited to std::numeric_limits<KeySizeT>::max() - 1.
* That is 65 535 characters by default, but can be raised with the KeySizeT template parameter.
* See max_key_size() for an easy access to this limit.
*
* Iterators invalidation:
* - clear, operator=: always invalidate the iterators.
* - insert, emplace, operator[]: always invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class CharT,
class T,
class Hash = tsl::ah::str_hash<CharT>,
class KeySizeT = std::uint16_t>
class htrie_map {
private:
template<typename U>
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, T, Hash, KeySizeT>;
public:
using char_type = typename ht::char_type;
using mapped_type = T;
using key_size_type = typename ht::key_size_type;
using size_type = typename ht::size_type;
using hasher = typename ht::hasher;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
using prefix_iterator = typename ht::prefix_iterator;
using const_prefix_iterator = typename ht::const_prefix_iterator;
public:
explicit htrie_map(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
ht::DEFAULT_BURST_THRESHOLD)
{
}
explicit htrie_map(size_type burst_threshold,
const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
burst_threshold)
{
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
htrie_map(InputIt first, InputIt last,
const Hash& hash = Hash()): htrie_map(hash)
{
insert(first, last);
}
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_map(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> init,
const Hash& hash = Hash()): htrie_map(hash)
{
insert(init);
}
#else
htrie_map(std::initializer_list<std::pair<const CharT*, T>> init,
const Hash& hash = Hash()): htrie_map(hash)
{
insert(init);
}
#endif
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
clear();
insert(ilist);
return *this;
}
#else
htrie_map& operator=(std::initializer_list<std::pair<const CharT*, T>> ilist) {
clear();
insert(ilist);
return *this;
}
#endif
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
/**
* Call shrink_to_fit() on each hash node of the hat-trie to reduce its size.
*/
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, const T& value) {
return m_ht.insert(key, key_size, value);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
return m_ht.insert(key.data(), key.size(), value);
}
#else
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
return m_ht.insert(key, std::strlen(key), value);
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
return m_ht.insert(key.data(), key.size(), value);
}
#endif
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
return m_ht.insert(key, key_size, std::move(value));
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
return m_ht.insert(key.data(), key.size(), std::move(value));
}
#else
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
return m_ht.insert(key, std::strlen(key), std::move(value));
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
return m_ht.insert(key.data(), key.size(), std::move(value));
}
#endif
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
void insert(InputIt first, InputIt last) {
for(auto it = first; it != last; ++it) {
insert_pair(*it);
}
}
#ifdef TSL_HT_HAS_STRING_VIEW
void insert(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
insert(ilist.begin(), ilist.end());
}
#else
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
insert(ilist.begin(), ilist.end());
}
#endif
template<class... Args>
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
return m_ht.insert(key, key_size, std::forward<Args>(args)...);
}
#ifdef TSL_HT_HAS_STRING_VIEW
template<class... Args>
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
return m_ht.insert(key.data(), key.size(), std::forward<Args>(args)...);
}
#else
template<class... Args>
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
return m_ht.insert(key, std::strlen(key), std::forward<Args>(args)...);
}
template<class... Args>
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
return m_ht.insert(key.data(), key.size(), std::forward<Args>(args)...);
}
#endif
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase_ks(const CharT* key, size_type key_size) {
return m_ht.erase(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
size_type erase(const std::basic_string_view<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#else
size_type erase(const CharT* key) {
return m_ht.erase(key, std::strlen(key));
}
size_type erase(const std::basic_string<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#endif
/**
* Erase all the elements which have 'prefix' as prefix. Return the number of erase elements.
*/
size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.erase_prefix(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string_view<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#else
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const CharT* prefix) {
return m_ht.erase_prefix(prefix, std::strlen(prefix));
}
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#endif
void swap(htrie_map& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
T& at_ks(const CharT* key, size_type key_size) { return m_ht.at(key, key_size); }
const T& at_ks(const CharT* key, size_type key_size) const { return m_ht.at(key, key_size); }
#ifdef TSL_HT_HAS_STRING_VIEW
T& at(const std::basic_string_view<CharT>& key) { return m_ht.at(key.data(), key.size()); }
const T& at(const std::basic_string_view<CharT>& key) const { return m_ht.at(key.data(), key.size()); }
#else
T& at(const CharT* key) { return m_ht.at(key, std::strlen(key)); }
const T& at(const CharT* key) const { return m_ht.at(key, std::strlen(key)); }
T& at(const std::basic_string<CharT>& key) { return m_ht.at(key.data(), key.size()); }
const T& at(const std::basic_string<CharT>& key) const { return m_ht.at(key.data(), key.size()); }
#endif
#ifdef TSL_HT_HAS_STRING_VIEW
T& operator[](const std::basic_string_view<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
#else
T& operator[](const CharT* key) { return m_ht.access_operator(key, std::strlen(key)); }
T& operator[](const std::basic_string<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
#endif
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
#ifdef TSL_HT_HAS_STRING_VIEW
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#else
size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); }
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#endif
iterator find_ks(const CharT* key, size_type key_size) {
return m_ht.find(key, key_size);
}
const_iterator find_ks(const CharT* key, size_type key_size) const {
return m_ht.find(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
iterator find(const std::basic_string_view<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string_view<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#else
iterator find(const CharT* key) {
return m_ht.find(key, std::strlen(key));
}
const_iterator find(const CharT* key) const {
return m_ht.find(key, std::strlen(key));
}
iterator find(const std::basic_string<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#endif
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
return m_ht.equal_range(key, key_size);
}
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
return m_ht.equal_range(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#else
std::pair<iterator, iterator> equal_range(const CharT* key) {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#endif
/**
* Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair
* of iterator, the first being the begin iterator and the second being the end iterator.
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#else
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const CharT* prefix) {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const CharT* prefix) const {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#endif
/**
* Return the element in the trie which is the longest prefix of `key`. If no
* element in the trie is a prefix of `key`, the end iterator is returned.
*
* Example:
*
* tsl::htrie_map<char, int> map = {{"/foo", 1}, {"/foo/bar", 1}};
*
* map.longest_prefix("/foo"); // returns {"/foo", 1}
* map.longest_prefix("/foo/baz"); // returns {"/foo", 1}
* map.longest_prefix("/foo/bar/baz"); // returns {"/foo/bar", 1}
* map.longest_prefix("/foo/bar/"); // returns {"/foo/bar", 1}
* map.longest_prefix("/bar"); // returns end()
* map.longest_prefix(""); // returns end()
*/
iterator longest_prefix_ks(const CharT* key, size_type key_size) {
return m_ht.longest_prefix(key, key_size);
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const {
return m_ht.longest_prefix(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string_view<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string_view<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#else
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const CharT* key) {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const CharT* key) const {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#endif
/*
* Hash policy
*/
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
/*
* Burst policy
*/
size_type burst_threshold() const { return m_ht.burst_threshold(); }
void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
/*
* Other
*/
/**
* Serialize the map through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the following calls:
* - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U.
* - `void operator()(const CharT* value, std::size_t value_size);`
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
* in the hands of the `Serializer` function object if compatibility is required.
*/
template<class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized map through the `deserializer` parameter.
*
* The `deserializer` parameter must be a function object that supports the following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U.
* - `void operator()(CharT* value_out, std::size_t value_size);`
*
* If the deserialized hash map part of the hat-trie is hash compatible with the serialized map, the deserialization process
* can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
* and KeySizeT must behave the same than the ones used in the serialized map. Otherwise the behaviour is undefined
* with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `CharT` and `T` of the `htrie_map` are not the same as the
* types used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
*/
template<class Deserializer>
static htrie_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
htrie_map map;
map.m_ht.deserialize(deserializer, hash_compatible);
return map;
}
friend bool operator==(const htrie_map& lhs, const htrie_map& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
std::string key_buffer;
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
it.key(key_buffer);
const auto it_element_rhs = rhs.find(key_buffer);
if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) {
return false;
}
}
return true;
}
friend bool operator!=(const htrie_map& lhs, const htrie_map& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(htrie_map& lhs, htrie_map& rhs) {
lhs.swap(rhs);
}
private:
template<class U, class V>
void insert_pair(const std::pair<U, V>& value) {
insert(value.first, value.second);
}
template<class U, class V>
void insert_pair(std::pair<U, V>&& value) {
insert(value.first, std::move(value.second));
}
private:
ht m_ht;
};
} // end namespace tsl
#endif

586
ios/include/tsl/htrie_set.h Normal file
View File

@@ -0,0 +1,586 @@
/**
* MIT License
*
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_HTRIE_SET_H
#define TSL_HTRIE_SET_H
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <string>
#include <utility>
#include "htrie_hash.h"
namespace tsl {
/**
* Implementation of a hat-trie set.
*
* The size of a key string is limited to std::numeric_limits<KeySizeT>::max() - 1.
* That is 65 535 characters by default, but can be raised with the KeySizeT template parameter.
* See max_key_size() for an easy access to this limit.
*
* Iterators invalidation:
* - clear, operator=: always invalidate the iterators.
* - insert: always invalidate the iterators.
* - erase: always invalidate the iterators.
*/
template<class CharT,
class Hash = tsl::ah::str_hash<CharT>,
class KeySizeT = std::uint16_t>
class htrie_set {
private:
template<typename U>
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
using ht = tsl::detail_htrie_hash::htrie_hash<CharT, void, Hash, KeySizeT>;
public:
using char_type = typename ht::char_type;
using key_size_type = typename ht::key_size_type;
using size_type = typename ht::size_type;
using hasher = typename ht::hasher;
using iterator = typename ht::iterator;
using const_iterator = typename ht::const_iterator;
using prefix_iterator = typename ht::prefix_iterator;
using const_prefix_iterator = typename ht::const_prefix_iterator;
public:
explicit htrie_set(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
ht::DEFAULT_BURST_THRESHOLD)
{
}
explicit htrie_set(size_type burst_threshold,
const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR,
burst_threshold)
{
}
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
htrie_set(InputIt first, InputIt last,
const Hash& hash = Hash()): htrie_set(hash)
{
insert(first, last);
}
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_set(std::initializer_list<std::basic_string_view<CharT>> init,
const Hash& hash = Hash()): htrie_set(hash)
{
insert(init);
}
#else
htrie_set(std::initializer_list<const CharT*> init,
const Hash& hash = Hash()): htrie_set(hash)
{
insert(init);
}
#endif
#ifdef TSL_HT_HAS_STRING_VIEW
htrie_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
clear();
insert(ilist);
return *this;
}
#else
htrie_set& operator=(std::initializer_list<const CharT*> ilist) {
clear();
insert(ilist);
return *this;
}
#endif
/*
* Iterators
*/
iterator begin() noexcept { return m_ht.begin(); }
const_iterator begin() const noexcept { return m_ht.begin(); }
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
iterator end() noexcept { return m_ht.end(); }
const_iterator end() const noexcept { return m_ht.end(); }
const_iterator cend() const noexcept { return m_ht.cend(); }
/*
* Capacity
*/
bool empty() const noexcept { return m_ht.empty(); }
size_type size() const noexcept { return m_ht.size(); }
size_type max_size() const noexcept { return m_ht.max_size(); }
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
/**
* Call shrink_to_fit() on each hash node of the hat-trie to reduce its size.
*/
void shrink_to_fit() { m_ht.shrink_to_fit(); }
/*
* Modifiers
*/
void clear() noexcept { m_ht.clear(); }
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
return m_ht.insert(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#else
std::pair<iterator, bool> insert(const CharT* key) {
return m_ht.insert(key, std::strlen(key));
}
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#endif
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
void insert(InputIt first, InputIt last) {
for(auto it = first; it != last; ++it) {
insert(*it);
}
}
#ifdef TSL_HT_HAS_STRING_VIEW
void insert(std::initializer_list<std::basic_string_view<CharT>> ilist) {
insert(ilist.begin(), ilist.end());
}
#else
void insert(std::initializer_list<const CharT*> ilist) {
insert(ilist.begin(), ilist.end());
}
#endif
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
return m_ht.insert(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#else
std::pair<iterator, bool> emplace(const CharT* key) {
return m_ht.insert(key, std::strlen(key));
}
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key) {
return m_ht.insert(key.data(), key.size());
}
#endif
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
size_type erase_ks(const CharT* key, size_type key_size) {
return m_ht.erase(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
size_type erase(const std::basic_string_view<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#else
size_type erase(const CharT* key) {
return m_ht.erase(key, std::strlen(key));
}
size_type erase(const std::basic_string<CharT>& key) {
return m_ht.erase(key.data(), key.size());
}
#endif
/**
* Erase all the elements which have 'prefix' as prefix. Return the number of erase elements.
*/
size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.erase_prefix(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string_view<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#else
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const CharT* prefix) {
return m_ht.erase_prefix(prefix, std::strlen(prefix));
}
/**
* @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size)
*/
size_type erase_prefix(const std::basic_string<CharT>& prefix) {
return m_ht.erase_prefix(prefix.data(), prefix.size());
}
#endif
void swap(htrie_set& other) { other.m_ht.swap(m_ht); }
/*
* Lookup
*/
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
#ifdef TSL_HT_HAS_STRING_VIEW
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#else
size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); }
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
#endif
iterator find_ks(const CharT* key, size_type key_size) {
return m_ht.find(key, key_size);
}
const_iterator find_ks(const CharT* key, size_type key_size) const {
return m_ht.find(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
iterator find(const std::basic_string_view<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string_view<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#else
iterator find(const CharT* key) {
return m_ht.find(key, std::strlen(key));
}
const_iterator find(const CharT* key) const {
return m_ht.find(key, std::strlen(key));
}
iterator find(const std::basic_string<CharT>& key) {
return m_ht.find(key.data(), key.size());
}
const_iterator find(const std::basic_string<CharT>& key) const {
return m_ht.find(key.data(), key.size());
}
#endif
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
return m_ht.equal_range(key, key_size);
}
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
return m_ht.equal_range(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#else
std::pair<iterator, iterator> equal_range(const CharT* key) {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
return m_ht.equal_range(key, std::strlen(key));
}
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
return m_ht.equal_range(key.data(), key.size());
}
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
return m_ht.equal_range(key.data(), key.size());
}
#endif
/**
* Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair
* of iterator, the first being the begin iterator and the second being the end iterator.
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const {
return m_ht.equal_prefix_range(prefix, prefix_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string_view<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#else
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const CharT* prefix) {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const CharT* prefix) const {
return m_ht.equal_prefix_range(prefix, std::strlen(prefix));
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<prefix_iterator, prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
/**
* @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size)
*/
std::pair<const_prefix_iterator, const_prefix_iterator> equal_prefix_range(const std::basic_string<CharT>& prefix) const {
return m_ht.equal_prefix_range(prefix.data(), prefix.size());
}
#endif
/**
* Return the element in the trie which is the longest prefix of `key`. If no
* element in the trie is a prefix of `key`, the end iterator is returned.
*
* Example:
*
* tsl::htrie_set<char> set = {"/foo", "/foo/bar"};
*
* set.longest_prefix("/foo"); // returns "/foo"
* set.longest_prefix("/foo/baz"); // returns "/foo"
* set.longest_prefix("/foo/bar/baz"); // returns "/foo/bar"
* set.longest_prefix("/foo/bar/"); // returns "/foo/bar"
* set.longest_prefix("/bar"); // returns end()
* set.longest_prefix(""); // returns end()
*/
iterator longest_prefix_ks(const CharT* key, size_type key_size) {
return m_ht.longest_prefix(key, key_size);
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const {
return m_ht.longest_prefix(key, key_size);
}
#ifdef TSL_HT_HAS_STRING_VIEW
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string_view<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string_view<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#else
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const CharT* key) {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const CharT* key) const {
return m_ht.longest_prefix(key, std::strlen(key));
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
iterator longest_prefix(const std::basic_string<CharT>& key) {
return m_ht.longest_prefix(key.data(), key.size());
}
/**
* @copydoc longest_prefix_ks(const CharT* key, size_type key_size)
*/
const_iterator longest_prefix(const std::basic_string<CharT>& key) const {
return m_ht.longest_prefix(key.data(), key.size());
}
#endif
/*
* Hash policy
*/
float max_load_factor() const { return m_ht.max_load_factor(); }
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
/*
* Burst policy
*/
size_type burst_threshold() const { return m_ht.burst_threshold(); }
void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); }
/*
* Observers
*/
hasher hash_function() const { return m_ht.hash_function(); }
/*
* Other
*/
/**
* Serialize the set through the `serializer` parameter.
*
* The `serializer` parameter must be a function object that supports the following calls:
* - `void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U.
* - `void operator()(const CharT* value, std::size_t value_size);`
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
* in the hands of the `Serializer` function object if compatibility is required.
*/
template<class Serializer>
void serialize(Serializer& serializer) const {
m_ht.serialize(serializer);
}
/**
* Deserialize a previously serialized set through the `deserializer` parameter.
*
* The `deserializer` parameter must be a function object that supports the following calls:
* - `template<typename U> U operator()();` where the types `std::uint64_t` and `float` must be supported for U.
* - `void operator()(CharT* value_out, std::size_t value_size);`
*
* If the deserialized hash set part of the hat-trie is hash compatible with the serialized set, the deserialization process
* can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
* and KeySizeT must behave the same than the ones used in the serialized set. Otherwise the behaviour is undefined
* with `hash_compatible` sets to true.
*
* The behaviour is undefined if the type `CharT` of the `htrie_set` is not the same as the
* type used during serialization.
*
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
*/
template<class Deserializer>
static htrie_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
htrie_set set;
set.m_ht.deserialize(deserializer, hash_compatible);
return set;
}
friend bool operator==(const htrie_set& lhs, const htrie_set& rhs) {
if(lhs.size() != rhs.size()) {
return false;
}
std::string key_buffer;
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
it.key(key_buffer);
const auto it_element_rhs = rhs.find(key_buffer);
if(it_element_rhs == rhs.cend()) {
return false;
}
}
return true;
}
friend bool operator!=(const htrie_set& lhs, const htrie_set& rhs) {
return !operator==(lhs, rhs);
}
friend void swap(htrie_set& lhs, htrie_set& rhs) {
lhs.swap(rhs);
}
private:
ht m_ht;
};
} // end namespace tsl
#endif

View File

@@ -1,290 +1,290 @@
/**
* MIT License
*
* Copyright (c) 2017 Tessil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_GROWTH_POLICY_H
#define TSL_ROBIN_GROWTH_POLICY_H
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstddef>
#include <iterator>
#include <limits>
#include <ratio>
#include <stdexcept>
#ifdef __EXCEPTIONS
# define THROW(_e, _m) throw _e(_m)
#else
# include <stdio.h>
# ifndef NDEBUG
# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0)
# else
# define THROW(_e, _m) std::terminate()
# endif
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_builtin(__builtin_expect)
# define TSL_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
#else
# define TSL_LIKELY( exp ) (exp)
#endif
namespace tsl {
namespace rh {
/**
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
*
* GrowthFactor must be a power of two >= 2.
*/
template<std::size_t GrowthFactor>
class power_of_two_growth_policy {
public:
/**
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
*/
power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0.");
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
m_mask = min_bucket_count_in_out - 1;
}
/**
* Return the bucket [0, bucket_count()) to which the hash belongs.
*/
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash & m_mask;
}
/**
* Return the bucket count to use when the bucket array grows on rehash.
*/
std::size_t next_bucket_count() const {
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
return (m_mask + 1) * GrowthFactor;
}
/**
* Return the maximum number of buckets supported by the policy.
*/
std::size_t max_bucket_count() const {
// Largest power of two.
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
}
private:
static std::size_t round_up_to_power_of_two(std::size_t value) {
if(is_power_of_two(value)) {
return value;
}
if(value == 0) {
return 1;
}
--value;
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
value |= value >> i;
}
return value + 1;
}
static constexpr bool is_power_of_two(std::size_t value) {
return value != 0 && (value & (value - 1)) == 0;
}
protected:
static const std::size_t MIN_BUCKETS_SIZE = 2;
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
std::size_t m_mask;
};
/**
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
* to a bucket. Slower but it can be usefull if you want a slower growth.
*/
template<class GrowthFactor = std::ratio<3, 2>>
class mod_growth_policy {
public:
mod_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0.");
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
m_bucket_count = min_bucket_count_in_out;
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash % m_bucket_count;
}
std::size_t next_bucket_count() const {
if(m_bucket_count == max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
const double next_bucket_count = std::ceil(double(m_bucket_count) * REHASH_SIZE_MULTIPLICATION_FACTOR);
if(!std::isnormal(next_bucket_count)) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
if(next_bucket_count > double(max_bucket_count())) {
return max_bucket_count();
}
else {
return std::size_t(next_bucket_count);
}
}
std::size_t max_bucket_count() const {
return MAX_BUCKET_COUNT;
}
private:
static const std::size_t MIN_BUCKETS_SIZE = 2;
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
static const std::size_t MAX_BUCKET_COUNT =
std::size_t(double(
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
));
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
std::size_t m_bucket_count;
};
namespace detail {
static constexpr const std::array<std::size_t, 39> PRIMES = {{
5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, 1543ul, 2053ul,
3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
}};
template<unsigned int IPrime>
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
// compiler can optimize the modulo code better with a constant known at the compilation.
static constexpr const std::array<std::size_t(*)(std::size_t), 39> MOD_PRIME = {{
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>
}};
}
/**
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in
* general but will probably distribute the values around better in the buckets with a poor hash function.
*
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
*
* With a switch the code would look like:
* \code
* switch(iprime) { // iprime is the current prime of the hash table
* case 0: hash % 5ul;
* break;
* case 1: hash % 17ul;
* break;
* case 2: hash % 29ul;
* break;
* ...
* }
* \endcode
*
* Due to the constant variable in the modulo the compiler is able to optimize the operation
* by a series of multiplications, substractions and shifts.
*
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
*/
class prime_growth_policy {
public:
prime_growth_policy(std::size_t& min_bucket_count_in_out) {
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
detail::PRIMES.end(), min_bucket_count_in_out);
if(it_prime == detail::PRIMES.end()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
min_bucket_count_in_out = *it_prime;
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return detail::MOD_PRIME[m_iprime](hash);
}
std::size_t next_bucket_count() const {
if(m_iprime + 1 >= detail::PRIMES.size()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
return detail::PRIMES[m_iprime + 1];
}
std::size_t max_bucket_count() const {
return detail::PRIMES.back();
}
private:
unsigned int m_iprime;
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
"The type of m_iprime is not big enough.");
};
}
}
#endif
/**
* MIT License
*
* Copyright (c) 2017 Tessil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef TSL_ROBIN_GROWTH_POLICY_H
#define TSL_ROBIN_GROWTH_POLICY_H
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstddef>
#include <iterator>
#include <limits>
#include <ratio>
#include <stdexcept>
#ifdef __EXCEPTIONS
# define THROW(_e, _m) throw _e(_m)
#else
# include <stdio.h>
# ifndef NDEBUG
# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0)
# else
# define THROW(_e, _m) std::terminate()
# endif
#endif
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if __has_builtin(__builtin_expect)
# define TSL_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
#else
# define TSL_LIKELY( exp ) (exp)
#endif
namespace tsl {
namespace rh {
/**
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
*
* GrowthFactor must be a power of two >= 2.
*/
template<std::size_t GrowthFactor>
class power_of_two_growth_policy {
public:
/**
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
*/
power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0.");
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
m_mask = min_bucket_count_in_out - 1;
}
/**
* Return the bucket [0, bucket_count()) to which the hash belongs.
*/
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash & m_mask;
}
/**
* Return the bucket count to use when the bucket array grows on rehash.
*/
std::size_t next_bucket_count() const {
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
return (m_mask + 1) * GrowthFactor;
}
/**
* Return the maximum number of buckets supported by the policy.
*/
std::size_t max_bucket_count() const {
// Largest power of two.
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
}
private:
static std::size_t round_up_to_power_of_two(std::size_t value) {
if(is_power_of_two(value)) {
return value;
}
if(value == 0) {
return 1;
}
--value;
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
value |= value >> i;
}
return value + 1;
}
static constexpr bool is_power_of_two(std::size_t value) {
return value != 0 && (value & (value - 1)) == 0;
}
protected:
static const std::size_t MIN_BUCKETS_SIZE = 2;
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
std::size_t m_mask;
};
/**
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
* to a bucket. Slower but it can be usefull if you want a slower growth.
*/
template<class GrowthFactor = std::ratio<3, 2>>
class mod_growth_policy {
public:
mod_growth_policy(std::size_t& min_bucket_count_in_out) {
if(min_bucket_count_in_out > max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0.");
const std::size_t min_bucket_count = MIN_BUCKETS_SIZE;
min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out);
m_bucket_count = min_bucket_count_in_out;
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return hash % m_bucket_count;
}
std::size_t next_bucket_count() const {
if(m_bucket_count == max_bucket_count()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
const double next_bucket_count = std::ceil(double(m_bucket_count) * REHASH_SIZE_MULTIPLICATION_FACTOR);
if(!std::isnormal(next_bucket_count)) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
if(next_bucket_count > double(max_bucket_count())) {
return max_bucket_count();
}
else {
return std::size_t(next_bucket_count);
}
}
std::size_t max_bucket_count() const {
return MAX_BUCKET_COUNT;
}
private:
static const std::size_t MIN_BUCKETS_SIZE = 2;
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
static const std::size_t MAX_BUCKET_COUNT =
std::size_t(double(
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
));
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
std::size_t m_bucket_count;
};
namespace detail {
static constexpr const std::array<std::size_t, 39> PRIMES = {{
5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, 1543ul, 2053ul,
3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul,
6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
}};
template<unsigned int IPrime>
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
// compiler can optimize the modulo code better with a constant known at the compilation.
static constexpr const std::array<std::size_t(*)(std::size_t), 39> MOD_PRIME = {{
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>
}};
}
/**
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in
* general but will probably distribute the values around better in the buckets with a poor hash function.
*
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
*
* With a switch the code would look like:
* \code
* switch(iprime) { // iprime is the current prime of the hash table
* case 0: hash % 5ul;
* break;
* case 1: hash % 17ul;
* break;
* case 2: hash % 29ul;
* break;
* ...
* }
* \endcode
*
* Due to the constant variable in the modulo the compiler is able to optimize the operation
* by a series of multiplications, substractions and shifts.
*
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement.
*/
class prime_growth_policy {
public:
prime_growth_policy(std::size_t& min_bucket_count_in_out) {
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
detail::PRIMES.end(), min_bucket_count_in_out);
if(it_prime == detail::PRIMES.end()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
min_bucket_count_in_out = *it_prime;
}
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
return detail::MOD_PRIME[m_iprime](hash);
}
std::size_t next_bucket_count() const {
if(m_iprime + 1 >= detail::PRIMES.size()) {
THROW(std::length_error, "The hash table exceeds its maxmimum size.");
}
return detail::PRIMES[m_iprime + 1];
}
std::size_t max_bucket_count() const {
return detail::PRIMES.back();
}
private:
unsigned int m_iprime;
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
"The type of m_iprime is not big enough.");
};
}
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +1,114 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_BINARYTREEARRAY_H
#define TNT_UTILS_BINARYTREEARRAY_H
#include <utils/compiler.h>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class BinaryTreeArray {
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_pod<TYPE>::value>::type>
class stack {
TYPE mElements[CAPACITY];
size_t mSize = 0;
public:
bool empty() const noexcept { return mSize == 0; }
void push(TYPE const& v) noexcept {
assert(mSize < CAPACITY);
mElements[mSize++] = v;
}
void pop() noexcept {
assert(mSize > 0);
--mSize;
}
const TYPE& back() const noexcept {
return mElements[mSize - 1];
}
};
public:
static size_t count(size_t height) noexcept { return (1u << height) - 1; }
static size_t left(size_t i, size_t height) noexcept { return i + 1; }
static size_t right(size_t i, size_t height) noexcept { return i + (1u << (height - 1)); }
// this builds the depth-first binary tree array top down (post-order)
template<typename Leaf, typename Node>
static void traverse(size_t height, Leaf leaf, Node node) noexcept {
struct TNode {
uint32_t index;
uint32_t col;
uint32_t height;
uint32_t next;
bool isLeaf() const noexcept { return height == 1; }
size_t left() const noexcept { return BinaryTreeArray::left(index, height); }
size_t right() const noexcept { return BinaryTreeArray::right(index, height); }
};
stack<TNode, 16> stack;
stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) });
uint32_t prevLeft = 0;
uint32_t prevRight = 0;
uint32_t prevIndex = 0;
while (!stack.empty()) {
TNode const* const UTILS_RESTRICT curr = &stack.back();
const bool isLeaf = curr->isLeaf();
const uint32_t index = curr->index;
const uint32_t l = (uint32_t)curr->left();
const uint32_t r = (uint32_t)curr->right();
if (prevLeft == index || prevRight == index) {
if (!isLeaf) {
// the 'next' node of our left node's right descendants is our right child
stack.push({ l, 2 * curr->col, curr->height - 1, r });
}
} else if (l == prevIndex) {
if (!isLeaf) {
// the 'next' node of our right child is our own 'next' sibling
stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next });
}
} else {
if (!isLeaf) {
node(index, l, r, curr->next);
} else {
leaf(index, curr->col, curr->next);
}
stack.pop();
}
prevLeft = l;
prevRight = r;
prevIndex = index;
}
}
};
} // namespace utils
#endif //TNT_UTILS_BINARYTREEARRAY_H
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_BINARYTREEARRAY_H
#define TNT_UTILS_BINARYTREEARRAY_H
#include <utils/compiler.h>
#include <type_traits>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
class BinaryTreeArray {
// Simple fixed capacity stack
template<typename TYPE, size_t CAPACITY,
typename = typename std::enable_if<std::is_pod<TYPE>::value>::type>
class stack {
TYPE mElements[CAPACITY];
size_t mSize = 0;
public:
bool empty() const noexcept { return mSize == 0; }
void push(TYPE const& v) noexcept {
assert(mSize < CAPACITY);
mElements[mSize++] = v;
}
void pop() noexcept {
assert(mSize > 0);
--mSize;
}
const TYPE& back() const noexcept {
return mElements[mSize - 1];
}
};
public:
static size_t count(size_t height) noexcept { return (1u << height) - 1; }
static size_t left(size_t i, size_t height) noexcept { return i + 1; }
static size_t right(size_t i, size_t height) noexcept { return i + (1u << (height - 1)); }
// this builds the depth-first binary tree array top down (post-order)
template<typename Leaf, typename Node>
static void traverse(size_t height, Leaf leaf, Node node) noexcept {
struct TNode {
uint32_t index;
uint32_t col;
uint32_t height;
uint32_t next;
bool isLeaf() const noexcept { return height == 1; }
size_t left() const noexcept { return BinaryTreeArray::left(index, height); }
size_t right() const noexcept { return BinaryTreeArray::right(index, height); }
};
stack<TNode, 16> stack;
stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) });
uint32_t prevLeft = 0;
uint32_t prevRight = 0;
uint32_t prevIndex = 0;
while (!stack.empty()) {
TNode const* const UTILS_RESTRICT curr = &stack.back();
const bool isLeaf = curr->isLeaf();
const uint32_t index = curr->index;
const uint32_t l = (uint32_t)curr->left();
const uint32_t r = (uint32_t)curr->right();
if (prevLeft == index || prevRight == index) {
if (!isLeaf) {
// the 'next' node of our left node's right descendants is our right child
stack.push({ l, 2 * curr->col, curr->height - 1, r });
}
} else if (l == prevIndex) {
if (!isLeaf) {
// the 'next' node of our right child is our own 'next' sibling
stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next });
}
} else {
if (!isLeaf) {
node(index, l, r, curr->next);
} else {
leaf(index, curr->col, curr->next);
}
stack.pop();
}
prevLeft = l;
prevRight = r;
prevIndex = index;
}
}
};
} // namespace utils
#endif //TNT_UTILS_BINARYTREEARRAY_H

View File

@@ -1,99 +1,99 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_BITMASKENUM_H
#define TNT_UTILS_BITMASKENUM_H
#include <utils/compiler.h>
#include <type_traits> // for std::false_type
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
template<typename Enum>
struct EnableBitMaskOperators : public std::false_type { };
} // namespace utils
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool operator!(Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return underlying(rhs) == 0;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator~(Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(~underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) | underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) & underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) ^ underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs | rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs & rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs ^ rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool none(Enum lhs) noexcept {
return !lhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool any(Enum lhs) noexcept {
return !none(lhs);
}
#endif // TNT_UTILS_BITMASKENUM_H
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_BITMASKENUM_H
#define TNT_UTILS_BITMASKENUM_H
#include <utils/compiler.h>
#include <type_traits> // for std::false_type
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
namespace utils {
template<typename Enum>
struct EnableBitMaskOperators : public std::false_type { };
} // namespace utils
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool operator!(Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return underlying(rhs) == 0;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator~(Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(~underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) | underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) & underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept {
using underlying = std::underlying_type_t<Enum>;
return Enum(underlying(lhs) ^ underlying(rhs));
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs | rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs & rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept {
return lhs = lhs ^ rhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool none(Enum lhs) noexcept {
return !lhs;
}
template<typename Enum, typename std::enable_if_t<
std::is_enum<Enum>::value && utils::EnableBitMaskOperators<Enum>::value, int> = 0>
inline constexpr bool any(Enum lhs) noexcept {
return !none(lhs);
}
#endif // TNT_UTILS_BITMASKENUM_H

View File

@@ -1,393 +1,393 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_CSTRING_H
#define TNT_FILAMENT_CSTRING_H
// NOTE: this header should not include STL headers
#include <utils/compiler.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
namespace utils {
//! \privatesection
struct hashCStrings {
typedef const char* argument_type;
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
}
};
//! \privatesection
struct equalCStrings {
typedef const char* first_argument_type;
typedef const char* second_argument_type;
typedef bool result_type;
bool operator()(const char* lhs, const char* rhs) const noexcept {
return !strcmp(lhs, rhs);
}
};
//! \privatesection
struct lessCStrings {
typedef const char* first_argument_type;
typedef const char* second_argument_type;
typedef bool result_type;
result_type operator()(first_argument_type lhs, second_argument_type rhs) const noexcept {
return strcmp(lhs, rhs) < 0;
}
};
// This can be used to creates a string from a string literal -- w/o underlying allocations.
// e.g.:
// StaticString s("Hello World!");
//
template <size_t N>
using StringLiteral = const char[N];
//! \publicsection
class UTILS_PUBLIC StaticString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using const_reference = const value_type&;
using const_pointer = const value_type*;
using const_iterator = const value_type*;
constexpr StaticString() noexcept = default;
// initialization from a string literal
template<size_t N>
constexpr StaticString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: mString(other),
mLength(size_type(N - 1)),
mHash(computeHash(other)) {
}
// assignment from a string literal
template<size_t N>
StaticString& operator=(StringLiteral<N> const& other) noexcept {
mString = other;
mLength = size_type(N - 1);
mHash = computeHash(other);
return *this;
}
// helper to make a StaticString from a C string that is known to be a string literal
static constexpr StaticString make(const_pointer literal, size_t length) noexcept {
StaticString r;
r.mString = literal;
r.mLength = size_type(length);
size_type hash = 5381;
while (int c = *literal++) {
hash = (hash * 33u) ^ size_type(c);
}
r.mHash = hash;
return r;
}
static StaticString make(const_pointer literal) noexcept {
return make(literal, strlen(literal));
}
const_pointer c_str() const noexcept { return mString; }
const_pointer data() const noexcept { return mString; }
size_type size() const noexcept { return mLength; }
size_type length() const noexcept { return mLength; }
bool empty() const noexcept { return size() == 0; }
void clear() noexcept { mString = nullptr; mLength = 0; }
const_iterator begin() const noexcept { return mString; }
const_iterator end() const noexcept { return mString + mLength; }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
size_type getHash() const noexcept { return mHash; }
private:
const_pointer mString = nullptr;
size_type mLength = 0;
size_type mHash = 0;
template<size_t N>
static constexpr size_type computeHash(StringLiteral<N> const& s) noexcept {
size_type hash = 5381;
for (size_t i = 0; i < N - 1; i++) {
hash = (hash * 33u) ^ size_type(s[i]);
}
return hash;
}
int compare(const StaticString& rhs) const noexcept;
friend bool operator==(StaticString const& lhs, StaticString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator!=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(StaticString const& lhs, StaticString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(StaticString const& lhs, StaticString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// ------------------------------------------------------------------------------------------------
class UTILS_PUBLIC CString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = value_type*;
using const_iterator = const value_type*;
CString() noexcept = default;
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
CString(const char* cstr, size_t length);
// Allocates memory and copies traditional C string content. Unlike the above constructor, this
// does not alllow embedded nulls. This is explicit because this operation is costly.
explicit CString(const char* cstr);
template<size_t N>
explicit CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: CString(other, N - 1) {
}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {}
CString(const CString& rhs);
CString(CString&& rhs) noexcept {
this->swap(rhs);
}
CString& operator=(const CString& rhs);
CString& operator=(CString&& rhs) noexcept {
this->swap(rhs);
return *this;
}
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
auto *temp = mCStr;
mCStr = other.mCStr;
other.mCStr = temp;
}
const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return mCStr; }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
reference front() noexcept {
assert(size());
return begin()[0];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
reference back() noexcept {
assert(size());
return begin()[size() - 1];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t size, void* ptr) {
assert(ptr);
return ptr;
}
private:
struct Data {
size_type length;
};
// mCStr points to the C-string or nullptr. if non-null, mCStr is preceded by the string's size
union {
value_type *mCStr = nullptr;
Data* mData; // Data is stored at mData[-1]
};
int compare(const CString& rhs) const noexcept {
size_type lhs_size = size();
size_type rhs_size = rhs.size();
if (lhs_size < rhs_size) return -1;
if (lhs_size > rhs_size) return 1;
return strncmp(data(), rhs.data(), size());
}
friend bool operator==(CString const& lhs, StaticString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile time failure.
template<typename T>
CString to_string(T value) noexcept;
} // namespace utils
// FIXME: how could we not include this one?
// needed for std::hash, since implementation is inline, this would not cause
// binaries incompatibilities if another STL version was used.
#include <functional>
namespace std {
//! \privatesection
template<>
struct hash<utils::CString> {
typedef utils::CString argument_type;
typedef size_t result_type;
utils::hashCStrings hasher;
size_t operator()(const utils::CString& s) const noexcept {
return hasher(s.c_str());
}
};
//! \privatesection
template<>
struct hash<utils::StaticString> {
typedef utils::StaticString argument_type;
typedef size_t result_type;
size_t operator()(const utils::StaticString& s) const noexcept {
return s.getHash();
}
};
} // namespace std
#endif // TNT_FILAMENT_CSTRING_H
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_CSTRING_H
#define TNT_UTILS_CSTRING_H
// NOTE: this header should not include STL headers
#include <utils/compiler.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
namespace utils {
//! \privatesection
struct hashCStrings {
typedef const char* argument_type;
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
}
};
//! \privatesection
struct equalCStrings {
typedef const char* first_argument_type;
typedef const char* second_argument_type;
typedef bool result_type;
bool operator()(const char* lhs, const char* rhs) const noexcept {
return !strcmp(lhs, rhs);
}
};
//! \privatesection
struct lessCStrings {
typedef const char* first_argument_type;
typedef const char* second_argument_type;
typedef bool result_type;
result_type operator()(first_argument_type lhs, second_argument_type rhs) const noexcept {
return strcmp(lhs, rhs) < 0;
}
};
// This can be used to creates a string from a string literal -- w/o underlying allocations.
// e.g.:
// StaticString s("Hello World!");
//
template <size_t N>
using StringLiteral = const char[N];
//! \publicsection
class UTILS_PUBLIC StaticString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using const_reference = const value_type&;
using const_pointer = const value_type*;
using const_iterator = const value_type*;
constexpr StaticString() noexcept = default;
// initialization from a string literal
template<size_t N>
constexpr StaticString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: mString(other),
mLength(size_type(N - 1)),
mHash(computeHash(other)) {
}
// assignment from a string literal
template<size_t N>
StaticString& operator=(StringLiteral<N> const& other) noexcept {
mString = other;
mLength = size_type(N - 1);
mHash = computeHash(other);
return *this;
}
// helper to make a StaticString from a C string that is known to be a string literal
static constexpr StaticString make(const_pointer literal, size_t length) noexcept {
StaticString r;
r.mString = literal;
r.mLength = size_type(length);
size_type hash = 5381;
while (int c = *literal++) {
hash = (hash * 33u) ^ size_type(c);
}
r.mHash = hash;
return r;
}
static StaticString make(const_pointer literal) noexcept {
return make(literal, strlen(literal));
}
const_pointer c_str() const noexcept { return mString; }
const_pointer data() const noexcept { return mString; }
size_type size() const noexcept { return mLength; }
size_type length() const noexcept { return mLength; }
bool empty() const noexcept { return size() == 0; }
void clear() noexcept { mString = nullptr; mLength = 0; }
const_iterator begin() const noexcept { return mString; }
const_iterator end() const noexcept { return mString + mLength; }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
size_type getHash() const noexcept { return mHash; }
private:
const_pointer mString = nullptr;
size_type mLength = 0;
size_type mHash = 0;
template<size_t N>
static constexpr size_type computeHash(StringLiteral<N> const& s) noexcept {
size_type hash = 5381;
for (size_t i = 0; i < N - 1; i++) {
hash = (hash * 33u) ^ size_type(s[i]);
}
return hash;
}
int compare(const StaticString& rhs) const noexcept;
friend bool operator==(StaticString const& lhs, StaticString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator!=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(StaticString const& lhs, StaticString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(StaticString const& lhs, StaticString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(StaticString const& lhs, StaticString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// ------------------------------------------------------------------------------------------------
class UTILS_PUBLIC CString {
public:
using value_type = char;
using size_type = uint32_t;
using difference_type = int32_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = value_type*;
using const_iterator = const value_type*;
CString() noexcept = default;
// Allocates memory and appends a null. This constructor can be used to hold arbitrary data
// inside the string (i.e. it can contain nulls or non-ASCII encodings).
CString(const char* cstr, size_t length);
// Allocates memory and copies traditional C string content. Unlike the above constructor, this
// does not alllow embedded nulls. This is explicit because this operation is costly.
explicit CString(const char* cstr);
template<size_t N>
explicit CString(StringLiteral<N> const& other) noexcept // NOLINT(google-explicit-constructor)
: CString(other, N - 1) {
}
CString(StaticString const& s) : CString(s.c_str(), s.size()) {}
CString(const CString& rhs);
CString(CString&& rhs) noexcept {
this->swap(rhs);
}
CString& operator=(const CString& rhs);
CString& operator=(CString&& rhs) noexcept {
this->swap(rhs);
return *this;
}
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
auto *temp = mCStr;
mCStr = other.mCStr;
other.mCStr = temp;
}
const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
size_type size() const noexcept { return mData ? mData[-1].length : 0; }
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return mCStr; }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
reference front() noexcept {
assert(size());
return begin()[0];
}
const_reference front() const noexcept {
assert(size());
return begin()[0];
}
reference back() noexcept {
assert(size());
return begin()[size() - 1];
}
const_reference back() const noexcept {
assert(size());
return begin()[size() - 1];
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t size, void* ptr) {
assert(ptr);
return ptr;
}
private:
struct Data {
size_type length;
};
// mCStr points to the C-string or nullptr. if non-null, mCStr is preceded by the string's size
union {
value_type *mCStr = nullptr;
Data* mData; // Data is stored at mData[-1]
};
int compare(const CString& rhs) const noexcept {
size_type lhs_size = size();
size_type rhs_size = rhs.size();
if (lhs_size < rhs_size) return -1;
if (lhs_size > rhs_size) return 1;
return strncmp(data(), rhs.data(), size());
}
friend bool operator==(CString const& lhs, StaticString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
}
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) < 0;
}
friend bool operator>(CString const& lhs, CString const& rhs) noexcept {
return lhs.compare(rhs) > 0;
}
friend bool operator>=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs < rhs);
}
friend bool operator<=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs > rhs);
}
};
// implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile time failure.
template<typename T>
CString to_string(T value) noexcept;
} // namespace utils
// FIXME: how could we not include this one?
// needed for std::hash, since implementation is inline, this would not cause
// binaries incompatibilities if another STL version was used.
#include <functional>
namespace std {
//! \privatesection
template<>
struct hash<utils::CString> {
typedef utils::CString argument_type;
typedef size_t result_type;
utils::hashCStrings hasher;
size_t operator()(const utils::CString& s) const noexcept {
return hasher(s.c_str());
}
};
//! \privatesection
template<>
struct hash<utils::StaticString> {
typedef utils::StaticString argument_type;
typedef size_t result_type;
size_t operator()(const utils::StaticString& s) const noexcept {
return s.getHash();
}
};
} // namespace std
#endif // TNT_UTILS_CSTRING_H

View File

@@ -1,127 +1,127 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_CALLSTACK_H_
#define UTILS_CALLSTACK_H_
#include <stddef.h>
#include <stdint.h>
#include <typeinfo>
#include <utils/CString.h>
#include <utils/Log.h>
namespace utils {
/**
* CallStack captures the current's thread call stack.
*/
class CallStack {
public:
/**
* Creates an empty call stack
* @see CallStack::capture()
*/
CallStack() = default;
CallStack(const CallStack&) = default;
~CallStack() = default;
/**
* A convenience method to create and capture the stack trace in one go.
* @param ignore number frames to ignore at the top of the stack.
* @return A CallStack object
*/
static CallStack unwind(size_t ignore = 0) noexcept;
/**
* Capture the current thread's stack and replaces the existing one if any.
* @param ignore number frames to ignore at the top of the stack.
*/
void update(size_t ignore = 0) noexcept;
/**
* Get the number of stack frames this object has recorded.
* @return How many stack frames are accessible through operator[]
*/
size_t getFrameCount() const noexcept;
/**
* Return the program-counter of each stack frame captured
* @param index of the frame between 0 and getFrameCount()-1
* @return the program-counter of the stack-frame recorded at index \p index
* @throw std::out_of_range if the index is out of range
*/
intptr_t operator [](size_t index) const;
/** Demangles a C++ type name */
static utils::CString demangleTypeName(const char* mangled);
template <typename T>
static utils::CString typeName() {
#if UTILS_HAS_RTTI
return demangleTypeName(typeid(T).name());
#else
return CString("<no-rtti>");
#endif
}
/**
* Outputs a CallStack into a stream.
* This will print, when possible, the demangled names of functions corresponding to the
* program-counter recorded.
*/
friend utils::io::ostream& operator <<(utils::io::ostream& stream, const CallStack& callstack);
bool operator <(const CallStack& rhs) const;
inline bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
inline bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
inline bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
inline bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
inline bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}
private:
void update_gcc(size_t ignore) noexcept;
static utils::CString demangle(const char* mangled);
static constexpr size_t NUM_FRAMES = 20;
struct StackFrameInfo {
intptr_t pc;
};
size_t m_frame_count = 0;
StackFrameInfo m_stack[NUM_FRAMES];
};
} // namespace utils
#endif // UTILS_CALLSTACK_H_
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_CALLSTACK_H
#define UTILS_CALLSTACK_H
#include <stddef.h>
#include <stdint.h>
#include <typeinfo>
#include <utils/CString.h>
#include <utils/Log.h>
namespace utils {
/**
* CallStack captures the current's thread call stack.
*/
class CallStack {
public:
/**
* Creates an empty call stack
* @see CallStack::capture()
*/
CallStack() = default;
CallStack(const CallStack&) = default;
~CallStack() = default;
/**
* A convenience method to create and capture the stack trace in one go.
* @param ignore number frames to ignore at the top of the stack.
* @return A CallStack object
*/
static CallStack unwind(size_t ignore = 0) noexcept;
/**
* Capture the current thread's stack and replaces the existing one if any.
* @param ignore number frames to ignore at the top of the stack.
*/
void update(size_t ignore = 0) noexcept;
/**
* Get the number of stack frames this object has recorded.
* @return How many stack frames are accessible through operator[]
*/
size_t getFrameCount() const noexcept;
/**
* Return the program-counter of each stack frame captured
* @param index of the frame between 0 and getFrameCount()-1
* @return the program-counter of the stack-frame recorded at index \p index
* @throw std::out_of_range if the index is out of range
*/
intptr_t operator [](size_t index) const;
/** Demangles a C++ type name */
static utils::CString demangleTypeName(const char* mangled);
template <typename T>
static utils::CString typeName() {
#if UTILS_HAS_RTTI
return demangleTypeName(typeid(T).name());
#else
return CString("<no-rtti>");
#endif
}
/**
* Outputs a CallStack into a stream.
* This will print, when possible, the demangled names of functions corresponding to the
* program-counter recorded.
*/
friend utils::io::ostream& operator <<(utils::io::ostream& stream, const CallStack& callstack);
bool operator <(const CallStack& rhs) const;
inline bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
inline bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
inline bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
inline bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
inline bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}
private:
void update_gcc(size_t ignore) noexcept;
static utils::CString demangle(const char* mangled);
static constexpr size_t NUM_FRAMES = 20;
struct StackFrameInfo {
intptr_t pc;
};
size_t m_frame_count = 0;
StackFrameInfo m_stack[NUM_FRAMES];
};
} // namespace utils
#endif // UTILS_CALLSTACK_H

View File

@@ -1,26 +1,26 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_CONDITION_H
#define UTILS_CONDITION_H
#if defined(__linux__) && !defined(__SANITIZE_THREAD__)
#include <utils/linux/Condition.h>
#else
#include <utils/generic/Condition.h>
#endif
#endif // UTILS_CONDITION_H
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_CONDITION_H
#define TNT_UTILS_CONDITION_H
#if defined(__linux__)
#include <utils/linux/Condition.h>
#else
#include <utils/generic/Condition.h>
#endif
#endif // TNT_UTILS_CONDITION_H

View File

@@ -1,91 +1,91 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_COUNTDOWNLATCH_H_
#define UTILS_COUNTDOWNLATCH_H_
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A count down latch is used to block one or several threads until the latch is signaled
* a certain number of times.
*
* Threads entering the latch are blocked until the latch is signaled enough times.
*
* @see CyclicBarrier
*/
class CountDownLatch {
public:
/**
* Creates a count down latch with a specified count. The minimum useful value is 1.
* @param count the latch counter initial value
*/
explicit CountDownLatch(size_t count) noexcept;
~CountDownLatch() = default;
/**
* Blocks until latch() is called \p count times.
* @see CountDownLatch(size_t count)
*/
void await() noexcept;
/**
* Releases threads blocked in await() when called \p count times. Calling latch() more than
* \p count times has no effect.
* @see reset()
*/
void latch() noexcept;
/**
* Resets the count-down latch to the given value.
*
* @param new_count New latch count. A value of zero will immediately unblock all waiting
* threads.
*
* @warning Use with caution. It's only safe to reset the latch count when you're sure
* that no threads are waiting in await(). This can be guaranteed in various ways, for
* instance, if you have a single thread calling await(), you could call reset() from that
* thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch
* are at a known place (i.e.: not in await()) when reset() is called.
*/
void reset(size_t new_count) noexcept;
/**
* @return the number of times latch() has been called since construction or reset.
* @see reset(), CountDownLatch(size_t count)
*/
size_t getCount() const noexcept;
CountDownLatch() = delete;
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
private:
uint32_t m_initial_count;
uint32_t m_remaining_count;
mutable Mutex m_lock;
mutable Condition m_cv;
};
} // namespace utils
#endif // UTILS_COUNTDOWNLATCH_H_
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_COUNTDOWNLATCH_H
#define TNT_UTILS_COUNTDOWNLATCH_H
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A count down latch is used to block one or several threads until the latch is signaled
* a certain number of times.
*
* Threads entering the latch are blocked until the latch is signaled enough times.
*
* @see CyclicBarrier
*/
class CountDownLatch {
public:
/**
* Creates a count down latch with a specified count. The minimum useful value is 1.
* @param count the latch counter initial value
*/
explicit CountDownLatch(size_t count) noexcept;
~CountDownLatch() = default;
/**
* Blocks until latch() is called \p count times.
* @see CountDownLatch(size_t count)
*/
void await() noexcept;
/**
* Releases threads blocked in await() when called \p count times. Calling latch() more than
* \p count times has no effect.
* @see reset()
*/
void latch() noexcept;
/**
* Resets the count-down latch to the given value.
*
* @param new_count New latch count. A value of zero will immediately unblock all waiting
* threads.
*
* @warning Use with caution. It's only safe to reset the latch count when you're sure
* that no threads are waiting in await(). This can be guaranteed in various ways, for
* instance, if you have a single thread calling await(), you could call reset() from that
* thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch
* are at a known place (i.e.: not in await()) when reset() is called.
*/
void reset(size_t new_count) noexcept;
/**
* @return the number of times latch() has been called since construction or reset.
* @see reset(), CountDownLatch(size_t count)
*/
size_t getCount() const noexcept;
CountDownLatch() = delete;
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
private:
uint32_t m_initial_count;
uint32_t m_remaining_count;
mutable Mutex m_lock;
mutable Condition m_cv;
};
} // namespace utils
#endif // TNT_UTILS_COUNTDOWNLATCH_H

View File

@@ -1,84 +1,84 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_CYCLIC_BARRIER_H_
#define UTILS_CYCLIC_BARRIER_H_
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A cyclic barrier is used to synchronize several threads to a particular execution point.
*
* Threads entering the barrier are halted until all threads reach the barrier.
*
* @see CountDownLatch
*/
class CyclicBarrier {
public:
/**
* Creates a cyclic barrier with a specified number of threads to synchronize. The minimum
* useful value is 2. A value of 0 is invalid and is silently changed to 1.
* @param num_threads Number of threads to synchronize.
*/
explicit CyclicBarrier(size_t num_threads) noexcept;
/**
* @return The number of thread that are synchronized.
*/
size_t getThreadCount() const noexcept;
/**
* @return Number of threads currently waiting on the barrier.
*/
size_t getWaitingThreadCount() const noexcept;
/**
* Blocks until getThreadCount()-1 other threads reach await().
*/
void await() noexcept;
/**
* Resets the cyclic barrier to its original state and releases all waiting threads.
*/
void reset() noexcept;
CyclicBarrier() = delete;
CyclicBarrier(const CyclicBarrier&) = delete;
CyclicBarrier& operator=(const CyclicBarrier&) = delete;
private:
enum class State {
TRAP, RELEASE
};
const size_t m_num_threads;
mutable Mutex m_lock;
mutable Condition m_cv;
State m_state = State::TRAP;
size_t m_trapped_threads = 0;
size_t m_released_threads = 0;
};
} // namespace utils
#endif // UTILS_CYCLIC_BARRIER_H_
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_CYCLIC_BARRIER_H
#define TNT_UTILS_CYCLIC_BARRIER_H
#include <stddef.h>
// note: we use our version of mutex/condition to keep this public header STL free
#include <utils/Condition.h>
#include <utils/Mutex.h>
namespace utils {
/**
* A cyclic barrier is used to synchronize several threads to a particular execution point.
*
* Threads entering the barrier are halted until all threads reach the barrier.
*
* @see CountDownLatch
*/
class CyclicBarrier {
public:
/**
* Creates a cyclic barrier with a specified number of threads to synchronize. The minimum
* useful value is 2. A value of 0 is invalid and is silently changed to 1.
* @param num_threads Number of threads to synchronize.
*/
explicit CyclicBarrier(size_t num_threads) noexcept;
/**
* @return The number of thread that are synchronized.
*/
size_t getThreadCount() const noexcept;
/**
* @return Number of threads currently waiting on the barrier.
*/
size_t getWaitingThreadCount() const noexcept;
/**
* Blocks until getThreadCount()-1 other threads reach await().
*/
void await() noexcept;
/**
* Resets the cyclic barrier to its original state and releases all waiting threads.
*/
void reset() noexcept;
CyclicBarrier() = delete;
CyclicBarrier(const CyclicBarrier&) = delete;
CyclicBarrier& operator=(const CyclicBarrier&) = delete;
private:
enum class State {
TRAP, RELEASE
};
const size_t m_num_threads;
mutable Mutex m_lock;
mutable Condition m_cv;
State m_state = State::TRAP;
size_t m_trapped_threads = 0;
size_t m_released_threads = 0;
};
} // namespace utils
#endif // TNT_UTILS_CYCLIC_BARRIER_H

View File

@@ -1,98 +1,98 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_ENTITY_H
#define TNT_UTILS_ENTITY_H
#include <utils/compiler.h>
// FIXME: could we get rid of <functional>
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
class UTILS_PUBLIC Entity {
public:
// this can be used to create an array of to-be-filled entities (see create())
Entity() noexcept = default;
// Entities can be copied
Entity(const Entity& e) noexcept = default;
Entity(Entity&& e) noexcept = default;
Entity& operator=(const Entity& e) noexcept = default;
Entity& operator=(Entity&& e) noexcept = default;
// Entities can be compared
bool operator==(Entity e) const { return e.mIdentity == mIdentity; }
bool operator!=(Entity e) const { return e.mIdentity != mIdentity; }
// Entities can be sorted
bool operator<(Entity e) const { return e.mIdentity < mIdentity; }
bool isNull() const noexcept {
return mIdentity == 0;
}
// an id that can be used for debugging/printing
uint32_t getId() const noexcept {
return mIdentity;
}
explicit operator bool() const noexcept { return !isNull(); }
void clear() noexcept { mIdentity = 0; }
// Exports an entity to an int32_t which can be used "as is" in the Java programing language.
static int32_t smuggle(Entity entity) noexcept {
return int32_t(entity.getId());
}
// Imports an entity from an int32_t generated by smuggle() above.
static Entity import(int32_t identity) noexcept {
return Entity{ Type(identity) };
}
private:
friend class EntityManager;
friend class EntityManagerImpl;
friend struct std::hash<Entity>;
using Type = uint32_t;
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
Type mIdentity = 0;
};
} // namespace utils
namespace std {
template<>
struct hash<utils::Entity> {
typedef utils::Entity argument_type;
typedef size_t result_type;
result_type operator()(argument_type const& e) const {
return e.getId();
}
};
} // namespace std
#endif // TNT_UTILS_ENTITY_H
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_ENTITY_H
#define TNT_UTILS_ENTITY_H
#include <utils/compiler.h>
// FIXME: could we get rid of <functional>
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
class UTILS_PUBLIC Entity {
public:
// this can be used to create an array of to-be-filled entities (see create())
Entity() noexcept = default;
// Entities can be copied
Entity(const Entity& e) noexcept = default;
Entity(Entity&& e) noexcept = default;
Entity& operator=(const Entity& e) noexcept = default;
Entity& operator=(Entity&& e) noexcept = default;
// Entities can be compared
bool operator==(Entity e) const { return e.mIdentity == mIdentity; }
bool operator!=(Entity e) const { return e.mIdentity != mIdentity; }
// Entities can be sorted
bool operator<(Entity e) const { return e.mIdentity < mIdentity; }
bool isNull() const noexcept {
return mIdentity == 0;
}
// an id that can be used for debugging/printing
uint32_t getId() const noexcept {
return mIdentity;
}
explicit operator bool() const noexcept { return !isNull(); }
void clear() noexcept { mIdentity = 0; }
// Exports an entity to an int32_t which can be used "as is" in the Java programing language.
static int32_t smuggle(Entity entity) noexcept {
return int32_t(entity.getId());
}
// Imports an entity from an int32_t generated by smuggle() above.
static Entity import(int32_t identity) noexcept {
return Entity{ Type(identity) };
}
private:
friend class EntityManager;
friend class EntityManagerImpl;
friend struct std::hash<Entity>;
using Type = uint32_t;
explicit Entity(Type identity) noexcept : mIdentity(identity) { }
Type mIdentity = 0;
};
} // namespace utils
namespace std {
template<>
struct hash<utils::Entity> {
typedef utils::Entity argument_type;
typedef size_t result_type;
result_type operator()(argument_type const& e) const {
return e.getId();
}
};
} // namespace std
#endif // TNT_UTILS_ENTITY_H

View File

@@ -1,89 +1,89 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_UTILS_ENTITYINSTANCE_H
#define TNT_FILAMENT_UTILS_ENTITYINSTANCE_H
#include <utils/compiler.h>
#include <type_traits>
#include <stdint.h>
namespace utils {
class UTILS_PUBLIC EntityInstanceBase {
public:
using Type = uint32_t;
protected:
Type mInstance = 0;
};
template <typename T, bool EDIT = false>
class UTILS_PUBLIC EntityInstance : public EntityInstanceBase {
public:
// default Instance is invalid
constexpr EntityInstance() noexcept = default;
// check if this Instance is valid
constexpr bool isValid() const noexcept { return mInstance != 0; }
// Instances of same type can be copied/assigned
constexpr EntityInstance(EntityInstance const& other) noexcept = default;
constexpr EntityInstance& operator=(EntityInstance const& other) noexcept = default;
// EDIT instances can be converted to "read" Instances of same type
template <typename = std::enable_if<!EDIT>>
constexpr explicit EntityInstance(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
}
template <typename = std::enable_if<!EDIT>>
EntityInstance& operator=(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
return *this;
}
// Instances can be compared
constexpr bool operator!=(EntityInstance e) const { return mInstance != e.mInstance; }
constexpr bool operator==(EntityInstance e) const { return mInstance == e.mInstance; }
// Instances can be sorted
constexpr bool operator<(EntityInstance e) const { return mInstance < e.mInstance; }
constexpr bool operator<=(EntityInstance e) const { return mInstance <= e.mInstance; }
constexpr bool operator>(EntityInstance e) const { return mInstance > e.mInstance; }
constexpr bool operator>=(EntityInstance e) const { return mInstance >= e.mInstance; }
// and we can iterate
constexpr EntityInstance& operator++() noexcept { ++mInstance; return *this; }
constexpr EntityInstance& operator--() noexcept { --mInstance; return *this; }
constexpr const EntityInstance operator++(int) const noexcept { return EntityInstance{ mInstance + 1 }; }
constexpr const EntityInstance operator--(int) const noexcept { return EntityInstance{ mInstance - 1 }; }
// return a value for this Instance (mostly needed for debugging
constexpr uint32_t asValue() const noexcept { return mInstance; }
// auto convert to Type so it can be used as an index
constexpr operator Type() const noexcept { return mInstance; } // NOLINT(google-explicit-constructor)
// conversion from Type so we can initialize from an index
constexpr EntityInstance(Type value) noexcept { mInstance = value; } // NOLINT(google-explicit-constructor)
};
} // namespace utils
#endif // TNT_FILAMENT_UTILS_ENTITYINSTANCE_H
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_ENTITYINSTANCE_H
#define TNT_UTILS_ENTITYINSTANCE_H
#include <utils/compiler.h>
#include <type_traits>
#include <stdint.h>
namespace utils {
class UTILS_PUBLIC EntityInstanceBase {
public:
using Type = uint32_t;
protected:
Type mInstance = 0;
};
template <typename T, bool EDIT = false>
class UTILS_PUBLIC EntityInstance : public EntityInstanceBase {
public:
// default Instance is invalid
constexpr EntityInstance() noexcept = default;
// check if this Instance is valid
constexpr bool isValid() const noexcept { return mInstance != 0; }
// Instances of same type can be copied/assigned
constexpr EntityInstance(EntityInstance const& other) noexcept = default;
constexpr EntityInstance& operator=(EntityInstance const& other) noexcept = default;
// EDIT instances can be converted to "read" Instances of same type
template <typename = std::enable_if<!EDIT>>
constexpr explicit EntityInstance(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
}
template <typename = std::enable_if<!EDIT>>
EntityInstance& operator=(EntityInstance<T, true> const& other) noexcept {
mInstance = other.asValue();
return *this;
}
// Instances can be compared
constexpr bool operator!=(EntityInstance e) const { return mInstance != e.mInstance; }
constexpr bool operator==(EntityInstance e) const { return mInstance == e.mInstance; }
// Instances can be sorted
constexpr bool operator<(EntityInstance e) const { return mInstance < e.mInstance; }
constexpr bool operator<=(EntityInstance e) const { return mInstance <= e.mInstance; }
constexpr bool operator>(EntityInstance e) const { return mInstance > e.mInstance; }
constexpr bool operator>=(EntityInstance e) const { return mInstance >= e.mInstance; }
// and we can iterate
constexpr EntityInstance& operator++() noexcept { ++mInstance; return *this; }
constexpr EntityInstance& operator--() noexcept { --mInstance; return *this; }
constexpr const EntityInstance operator++(int) const noexcept { return EntityInstance{ mInstance + 1 }; }
constexpr const EntityInstance operator--(int) const noexcept { return EntityInstance{ mInstance - 1 }; }
// return a value for this Instance (mostly needed for debugging
constexpr uint32_t asValue() const noexcept { return mInstance; }
// auto convert to Type so it can be used as an index
constexpr operator Type() const noexcept { return mInstance; } // NOLINT(google-explicit-constructor)
// conversion from Type so we can initialize from an index
constexpr EntityInstance(Type value) noexcept { mInstance = value; } // NOLINT(google-explicit-constructor)
};
} // namespace utils
#endif // TNT_UTILS_ENTITYINSTANCE_H

View File

@@ -1,133 +1,133 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_ENTITYMANAGER_H
#define TNT_UTILS_ENTITYMANAGER_H
#include <assert.h>
#include <stdint.h>
#include <utils/Entity.h>
#include <utils/compiler.h>
#ifndef FILAMENT_UTILS_TRACK_ENTITIES
#define FILAMENT_UTILS_TRACK_ENTITIES false
#endif
#if FILAMENT_UTILS_TRACK_ENTITIES
#include <utils/ostream.h>
#include <vector>
#endif
namespace utils {
class UTILS_PUBLIC EntityManager {
public:
// Get the global EntityManager. Is is recommended to cache this value.
// Thread Safe.
static EntityManager& get() noexcept;
class Listener {
public:
virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0;
protected:
~Listener() noexcept;
};
// maximum number of entities that can exist at the same time
static size_t getMaxEntityCount() noexcept {
// because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices
return RAW_INDEX_COUNT - 1;
}
// create n entities. Thread safe.
void create(size_t n, Entity* entities);
// destroys n entities. Thread safe.
void destroy(size_t n, Entity* entities) noexcept;
// create a new Entity. Thread safe.
// Return Entity.isNull() if the entity cannot be allocated.
Entity create() {
Entity e;
create(1, &e);
return e;
}
// destroys an Entity. Thread safe.
void destroy(Entity e) noexcept {
destroy(1, &e);
}
// return whether the given Entity has been destroyed (false) or not (true).
// Thread safe.
bool isAlive(Entity e) const noexcept {
assert(getIndex(e) < RAW_INDEX_COUNT);
return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]);
}
// registers a listener to be called when an entity is destroyed. thread safe.
// if the listener is already register, this method has no effect.
void registerListener(Listener* l) noexcept;
// unregisters a listener.
void unregisterListener(Listener* l) noexcept;
/* no user serviceable parts below */
// current generation of the given index. Use for debugging and testing.
uint8_t getGenerationForIndex(size_t index) const noexcept {
return mGens[index];
}
// singleton, can't be copied
EntityManager(const EntityManager& rhs) = delete;
EntityManager& operator=(const EntityManager& rhs) = delete;
#if FILAMENT_UTILS_TRACK_ENTITIES
std::vector<Entity> getActiveEntities() const;
void dumpActiveEntities(utils::io::ostream& out) const;
#endif
private:
friend class EntityManagerImpl;
EntityManager();
~EntityManager();
// GENERATION_SHIFT determines how many simultaneous Entities are available, the
// minimum memory requirement is 2^GENERATION_SHIFT bytes.
static constexpr const int GENERATION_SHIFT = 17;
static constexpr const size_t RAW_INDEX_COUNT = (1 << GENERATION_SHIFT);
static constexpr const Entity::Type INDEX_MASK = (1 << GENERATION_SHIFT) - 1u;
static inline Entity::Type getGeneration(Entity e) noexcept {
return e.getId() >> GENERATION_SHIFT;
}
static inline Entity::Type getIndex(Entity e) noexcept {
return e.getId() & INDEX_MASK;
}
static inline Entity::Type makeIdentity(Entity::Type g, Entity::Type i) noexcept {
return (g << GENERATION_SHIFT) | (i & INDEX_MASK);
}
// stores the generation of each index.
uint8_t * const mGens;
};
} // namespace utils
#endif // TNT_UTILS_ENTITYMANAGER_H
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_ENTITYMANAGER_H
#define TNT_UTILS_ENTITYMANAGER_H
#include <assert.h>
#include <stdint.h>
#include <utils/Entity.h>
#include <utils/compiler.h>
#ifndef FILAMENT_UTILS_TRACK_ENTITIES
#define FILAMENT_UTILS_TRACK_ENTITIES false
#endif
#if FILAMENT_UTILS_TRACK_ENTITIES
#include <utils/ostream.h>
#include <vector>
#endif
namespace utils {
class UTILS_PUBLIC EntityManager {
public:
// Get the global EntityManager. Is is recommended to cache this value.
// Thread Safe.
static EntityManager& get() noexcept;
class Listener {
public:
virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0;
protected:
~Listener() noexcept;
};
// maximum number of entities that can exist at the same time
static size_t getMaxEntityCount() noexcept {
// because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices
return RAW_INDEX_COUNT - 1;
}
// create n entities. Thread safe.
void create(size_t n, Entity* entities);
// destroys n entities. Thread safe.
void destroy(size_t n, Entity* entities) noexcept;
// create a new Entity. Thread safe.
// Return Entity.isNull() if the entity cannot be allocated.
Entity create() {
Entity e;
create(1, &e);
return e;
}
// destroys an Entity. Thread safe.
void destroy(Entity e) noexcept {
destroy(1, &e);
}
// return whether the given Entity has been destroyed (false) or not (true).
// Thread safe.
bool isAlive(Entity e) const noexcept {
assert(getIndex(e) < RAW_INDEX_COUNT);
return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]);
}
// registers a listener to be called when an entity is destroyed. thread safe.
// if the listener is already register, this method has no effect.
void registerListener(Listener* l) noexcept;
// unregisters a listener.
void unregisterListener(Listener* l) noexcept;
/* no user serviceable parts below */
// current generation of the given index. Use for debugging and testing.
uint8_t getGenerationForIndex(size_t index) const noexcept {
return mGens[index];
}
// singleton, can't be copied
EntityManager(const EntityManager& rhs) = delete;
EntityManager& operator=(const EntityManager& rhs) = delete;
#if FILAMENT_UTILS_TRACK_ENTITIES
std::vector<Entity> getActiveEntities() const;
void dumpActiveEntities(utils::io::ostream& out) const;
#endif
private:
friend class EntityManagerImpl;
EntityManager();
~EntityManager();
// GENERATION_SHIFT determines how many simultaneous Entities are available, the
// minimum memory requirement is 2^GENERATION_SHIFT bytes.
static constexpr const int GENERATION_SHIFT = 17;
static constexpr const size_t RAW_INDEX_COUNT = (1 << GENERATION_SHIFT);
static constexpr const Entity::Type INDEX_MASK = (1 << GENERATION_SHIFT) - 1u;
static inline Entity::Type getGeneration(Entity e) noexcept {
return e.getId() >> GENERATION_SHIFT;
}
static inline Entity::Type getIndex(Entity e) noexcept {
return e.getId() & INDEX_MASK;
}
static inline Entity::Type makeIdentity(Entity::Type g, Entity::Type i) noexcept {
return (g << GENERATION_SHIFT) | (i & INDEX_MASK);
}
// stores the generation of each index.
uint8_t * const mGens;
};
} // namespace utils
#endif // TNT_UTILS_ENTITYMANAGER_H

View File

@@ -1,410 +1,411 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_FIXEDCAPACITYVECTOR_H
#define TNT_UTILS_FIXEDCAPACITYVECTOR_H
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#ifndef NDEBUG
#define FILAMENT_FORCE_CAPACITY_CHECK true
#else
#define FILAMENT_FORCE_CAPACITY_CHECK false
#endif
namespace utils {
/**
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
* is called. Operations that add elements to the vector can fail if there is not enough
* capacity.
*
* An empty vector with a given capacity is created with
* FixedCapacityVector<T>::with_capacity( capacity );
*
* NOTE: When passing an initial size into the FixedCapacityVector constructor, default construction
* of the elements is skipped when their construction is trivial. This behavior is different from
* std::vector. e.g., std::vector<int>(4) constructs 4 zeros while FixedCapacityVector<int>(4)
* allocates 4 uninitialized values. Note that zero initialization is easily achieved by passing in
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
*/
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
class UTILS_PUBLIC FixedCapacityVector {
public:
using allocator_type = A;
using value_type = T;
using reference = T&;
using const_reference = T const&;
using size_type = uint32_t;
using difference_type = int32_t;
using pointer = T*;
using const_pointer = T const*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
private:
using storage_traits = std::allocator_traits<allocator_type>;
public:
/** returns an empty vector with the specified capacity */
static FixedCapacityVector with_capacity(
size_type capacity, const allocator_type& allocator = allocator_type()) {
return FixedCapacityVector(construct_with_capacity, capacity, allocator);
}
FixedCapacityVector() = default;
explicit FixedCapacityVector(const allocator_type& allocator) noexcept
: mCapacityAllocator({}, allocator) {
}
explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type())
: mSize(size),
mCapacityAllocator(size, allocator) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end());
}
FixedCapacityVector(size_type size, const_reference value,
const allocator_type& alloc = allocator_type())
: mSize(size),
mCapacityAllocator(size, alloc) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end(), value);
}
FixedCapacityVector(FixedCapacityVector const& rhs)
: mSize(rhs.mSize),
mCapacityAllocator(rhs.capacity(),
storage_traits::select_on_container_copy_construction(rhs.allocator())) {
mData = allocator().allocate(capacity());
std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
}
FixedCapacityVector(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
}
~FixedCapacityVector() noexcept {
destroy(begin(), end());
allocator().deallocate(data(), capacity());
}
FixedCapacityVector& operator=(FixedCapacityVector const& rhs) {
if (this != &rhs) {
FixedCapacityVector t(rhs);
this->swap(t);
}
return *this;
}
FixedCapacityVector& operator=(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
return *this;
}
allocator_type get_allocator() const noexcept {
return mCapacityAllocator.second();
}
// --------------------------------------------------------------------------------------------
iterator begin() noexcept { return data(); }
iterator end() noexcept { return data() + size(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return data() + size(); }
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
const_reverse_iterator crend() const noexcept { return rend(); }
// --------------------------------------------------------------------------------------------
size_type size() const noexcept { return mSize; }
size_type capacity() const noexcept { return mCapacityAllocator.first(); }
bool empty() const noexcept { return size() == 0; }
size_type max_size() const noexcept {
return std::min(storage_traits::max_size(allocator()),
std::numeric_limits<size_type>::max());
}
// --------------------------------------------------------------------------------------------
reference operator[](size_type n) noexcept {
assert(n < size());
return *(begin() + n);
}
const_reference operator[](size_type n) const noexcept {
assert(n < size());
return *(begin() + n);
}
reference front() noexcept { return *begin(); }
const_reference front() const noexcept { return *begin(); }
reference back() noexcept { return *(end() - 1); }
const_reference back() const noexcept { return *(end() - 1); }
value_type* data() noexcept { return mData; }
const value_type* data() const noexcept { return mData; }
// --------------------------------------------------------------------------------------------
void push_back(const_reference v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, v);
}
void push_back(value_type&& v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::move(v));
}
template<typename ... ARGS>
reference emplace_back(ARGS&& ... args) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::forward<ARGS>(args)...);
return *pos;
}
void pop_back() {
assert(!empty());
--mSize;
destroy(end(), end() + 1);
}
iterator insert(const_iterator position, const_reference v) {
if (position == end()) {
push_back(v);
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
// here we handle inserting an element of this vector!
const_pointer pv = std::addressof(v);
if (p <= pv && pv < end()) {
*p = *(pv + 1);
} else {
*p = v;
}
}
return const_cast<iterator>(position);
}
iterator insert(const_iterator position, value_type&& v) {
if (position == end()) {
push_back(std::move(v));
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
*p = std::move(v);
}
return const_cast<iterator>(position);
}
iterator erase(const_iterator pos) {
assert(pos != end());
return erase(pos, pos + 1);
}
iterator erase(const_iterator first, const_iterator last) {
assert(first <= last);
auto e = std::move(const_cast<iterator>(last), end(), const_cast<iterator>(first));
destroy(e, end());
mSize -= std::distance(first, last);
return const_cast<iterator>(first);
}
void clear() noexcept {
destroy(begin(), end());
mSize = 0;
}
void resize(size_type count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
// we check for triviality here so that the implementation could be non-inline
mSize = count;
} else {
resize_non_trivial(count);
}
}
void resize(size_type count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
mCapacityAllocator.swap(other.mCapacityAllocator);
}
UTILS_NOINLINE
void reserve(size_type c) {
if (c > capacity()) {
FixedCapacityVector t(construct_with_capacity, c, allocator());
t.mSize = size();
std::uninitialized_move(begin(), end(), t.begin());
this->swap(t);
}
}
private:
enum construct_with_capacity_tag{ construct_with_capacity };
FixedCapacityVector(construct_with_capacity_tag,
size_type capacity, const allocator_type& allocator = allocator_type())
: mCapacityAllocator(capacity, allocator) {
mData = this->allocator().allocate(this->capacity());
}
allocator_type& allocator() noexcept {
return mCapacityAllocator.second();
}
allocator_type const& allocator() const noexcept {
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
ASSERT_PRECONDITION(capacity() >= s,
"capacity exceeded: requested size %lu, available capacity %lu.",
(unsigned long)s, (unsigned long)capacity());
}
return end();
}
inline void construct(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_constructible_v<value_type>) {
construct_non_trivial(first, last);
}
}
void construct(iterator first, iterator last, const_reference proto) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++, proto);
}
}
// should this be NOINLINE?
void construct_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++);
}
}
inline void destroy(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_destructible_v<value_type>) {
destroy_non_trivial(first, last);
}
}
// should this be NOINLINE?
void destroy_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::destroy(allocator(), --last);
}
}
// should this be NOINLINE?
void resize_non_trivial(size_type count) {
if (count > size()) {
construct(end(), begin() + count);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void resize_non_trivial(size_type count, const_reference v) {
if (count > size()) {
construct(end(), begin() + count, v);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void move_range(pointer s, pointer e, pointer to) {
if constexpr(std::is_trivially_copy_assignable_v<value_type>
&& std::is_trivially_destructible_v<value_type>) {
// this generates memmove -- which doesn't happen otherwise
std::move_backward(s, e, to + std::distance(s, e));
} else {
pointer our_end = end();
difference_type n = our_end - to; // nb of elements to move by operator=
pointer i = s + n; // 1st element to move by move ctor
for (pointer d = our_end ; i < our_end ; ++i, ++d) {
storage_traits::construct(allocator(), d, std::move(*i));
}
std::move_backward(s, s + n, our_end);
}
}
template<typename TYPE>
class SizeTypeWrapper {
TYPE value{};
public:
SizeTypeWrapper() noexcept = default;
SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default;
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
SizeTypeWrapper operator=(TYPE rhs) noexcept { value = rhs; return *this; }
operator TYPE() const noexcept { return value; }
};
pointer mData{};
size_type mSize{};
compressed_pair<SizeTypeWrapper<size_type>, allocator_type> mCapacityAllocator{};
};
} // namespace utils
#endif //TNT_UTILS_FIXEDCAPACITYVECTOR_H
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_FIXEDCAPACITYVECTOR_H
#define TNT_UTILS_FIXEDCAPACITYVECTOR_H
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <algorithm>
#include <limits>
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
#ifndef NDEBUG
#define FILAMENT_FORCE_CAPACITY_CHECK true
#else
#define FILAMENT_FORCE_CAPACITY_CHECK false
#endif
namespace utils {
/**
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
* is called. Operations that add elements to the vector can fail if there is not enough
* capacity.
*
* An empty vector with a given capacity is created with
* FixedCapacityVector<T>::with_capacity( capacity );
*
* NOTE: When passing an initial size into the FixedCapacityVector constructor, default construction
* of the elements is skipped when their construction is trivial. This behavior is different from
* std::vector. e.g., std::vector<int>(4) constructs 4 zeros while FixedCapacityVector<int>(4)
* allocates 4 uninitialized values. Note that zero initialization is easily achieved by passing in
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
*/
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
class UTILS_PUBLIC FixedCapacityVector {
public:
using allocator_type = A;
using value_type = T;
using reference = T&;
using const_reference = T const&;
using size_type = uint32_t;
using difference_type = int32_t;
using pointer = T*;
using const_pointer = T const*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
private:
using storage_traits = std::allocator_traits<allocator_type>;
public:
/** returns an empty vector with the specified capacity */
static FixedCapacityVector with_capacity(
size_type capacity, const allocator_type& allocator = allocator_type()) {
return FixedCapacityVector(construct_with_capacity, capacity, allocator);
}
FixedCapacityVector() = default;
explicit FixedCapacityVector(const allocator_type& allocator) noexcept
: mCapacityAllocator({}, allocator) {
}
explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type())
: mSize(size),
mCapacityAllocator(size, allocator) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end());
}
FixedCapacityVector(size_type size, const_reference value,
const allocator_type& alloc = allocator_type())
: mSize(size),
mCapacityAllocator(size, alloc) {
mData = this->allocator().allocate(this->capacity());
construct(begin(), end(), value);
}
FixedCapacityVector(FixedCapacityVector const& rhs)
: mSize(rhs.mSize),
mCapacityAllocator(rhs.capacity(),
storage_traits::select_on_container_copy_construction(rhs.allocator())) {
mData = allocator().allocate(capacity());
std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
}
FixedCapacityVector(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
}
~FixedCapacityVector() noexcept {
destroy(begin(), end());
allocator().deallocate(data(), capacity());
}
FixedCapacityVector& operator=(FixedCapacityVector const& rhs) {
if (this != &rhs) {
FixedCapacityVector t(rhs);
this->swap(t);
}
return *this;
}
FixedCapacityVector& operator=(FixedCapacityVector&& rhs) noexcept {
this->swap(rhs);
return *this;
}
allocator_type get_allocator() const noexcept {
return mCapacityAllocator.second();
}
// --------------------------------------------------------------------------------------------
iterator begin() noexcept { return data(); }
iterator end() noexcept { return data() + size(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return data() + size(); }
reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
const_reverse_iterator crbegin() const noexcept { return rbegin(); }
const_reverse_iterator crend() const noexcept { return rend(); }
// --------------------------------------------------------------------------------------------
size_type size() const noexcept { return mSize; }
size_type capacity() const noexcept { return mCapacityAllocator.first(); }
bool empty() const noexcept { return size() == 0; }
size_type max_size() const noexcept {
return std::min(storage_traits::max_size(allocator()),
std::numeric_limits<size_type>::max());
}
// --------------------------------------------------------------------------------------------
reference operator[](size_type n) noexcept {
assert(n < size());
return *(begin() + n);
}
const_reference operator[](size_type n) const noexcept {
assert(n < size());
return *(begin() + n);
}
reference front() noexcept { return *begin(); }
const_reference front() const noexcept { return *begin(); }
reference back() noexcept { return *(end() - 1); }
const_reference back() const noexcept { return *(end() - 1); }
value_type* data() noexcept { return mData; }
const value_type* data() const noexcept { return mData; }
// --------------------------------------------------------------------------------------------
void push_back(const_reference v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, v);
}
void push_back(value_type&& v) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::move(v));
}
template<typename ... ARGS>
reference emplace_back(ARGS&& ... args) {
auto pos = assertCapacityForSize(size() + 1);
++mSize;
storage_traits::construct(allocator(), pos, std::forward<ARGS>(args)...);
return *pos;
}
void pop_back() {
assert(!empty());
--mSize;
destroy(end(), end() + 1);
}
iterator insert(const_iterator position, const_reference v) {
if (position == end()) {
push_back(v);
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
// here we handle inserting an element of this vector!
const_pointer pv = std::addressof(v);
if (p <= pv && pv < end()) {
*p = *(pv + 1);
} else {
*p = v;
}
}
return const_cast<iterator>(position);
}
iterator insert(const_iterator position, value_type&& v) {
if (position == end()) {
push_back(std::move(v));
} else {
assertCapacityForSize(size() + 1);
pointer p = const_cast<pointer>(position);
move_range(p, end(), p + 1);
++mSize;
*p = std::move(v);
}
return const_cast<iterator>(position);
}
iterator erase(const_iterator pos) {
assert(pos != end());
return erase(pos, pos + 1);
}
iterator erase(const_iterator first, const_iterator last) {
assert(first <= last);
auto e = std::move(const_cast<iterator>(last), end(), const_cast<iterator>(first));
destroy(e, end());
mSize -= std::distance(first, last);
return const_cast<iterator>(first);
}
void clear() noexcept {
destroy(begin(), end());
mSize = 0;
}
void resize(size_type count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
// we check for triviality here so that the implementation could be non-inline
mSize = count;
} else {
resize_non_trivial(count);
}
}
void resize(size_type count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
mCapacityAllocator.swap(other.mCapacityAllocator);
}
UTILS_NOINLINE
void reserve(size_type c) {
if (c > capacity()) {
FixedCapacityVector t(construct_with_capacity, c, allocator());
t.mSize = size();
std::uninitialized_move(begin(), end(), t.begin());
this->swap(t);
}
}
private:
enum construct_with_capacity_tag{ construct_with_capacity };
FixedCapacityVector(construct_with_capacity_tag,
size_type capacity, const allocator_type& allocator = allocator_type())
: mCapacityAllocator(capacity, allocator) {
mData = this->allocator().allocate(this->capacity());
}
allocator_type& allocator() noexcept {
return mCapacityAllocator.second();
}
allocator_type const& allocator() const noexcept {
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
ASSERT_PRECONDITION(capacity() >= s,
"capacity exceeded: requested size %lu, available capacity %lu.",
(unsigned long)s, (unsigned long)capacity());
}
return end();
}
inline void construct(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_constructible_v<value_type>) {
construct_non_trivial(first, last);
}
}
void construct(iterator first, iterator last, const_reference proto) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++, proto);
}
}
// should this be NOINLINE?
void construct_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::construct(allocator(), first++);
}
}
inline void destroy(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_destructible_v<value_type>) {
destroy_non_trivial(first, last);
}
}
// should this be NOINLINE?
void destroy_non_trivial(iterator first, iterator last) noexcept {
#pragma nounroll
while (first != last) {
storage_traits::destroy(allocator(), --last);
}
}
// should this be NOINLINE?
void resize_non_trivial(size_type count) {
if (count > size()) {
construct(end(), begin() + count);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void resize_non_trivial(size_type count, const_reference v) {
if (count > size()) {
construct(end(), begin() + count, v);
} else if (count < size()) {
destroy(begin() + count, end());
}
mSize = count;
}
// should this be NOINLINE?
void move_range(pointer s, pointer e, pointer to) {
if constexpr(std::is_trivially_copy_assignable_v<value_type>
&& std::is_trivially_destructible_v<value_type>) {
// this generates memmove -- which doesn't happen otherwise
std::move_backward(s, e, to + std::distance(s, e));
} else {
pointer our_end = end();
difference_type n = our_end - to; // nb of elements to move by operator=
pointer i = s + n; // 1st element to move by move ctor
for (pointer d = our_end ; i < our_end ; ++i, ++d) {
storage_traits::construct(allocator(), d, std::move(*i));
}
std::move_backward(s, s + n, our_end);
}
}
template<typename TYPE>
class SizeTypeWrapper {
TYPE value{};
public:
SizeTypeWrapper() noexcept = default;
SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default;
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
SizeTypeWrapper operator=(TYPE rhs) noexcept { value = rhs; return *this; }
operator TYPE() const noexcept { return value; }
};
pointer mData{};
size_type mSize{};
compressed_pair<SizeTypeWrapper<size_type>, allocator_type> mCapacityAllocator{};
};
} // namespace utils
#endif // TNT_UTILS_FIXEDCAPACITYVECTOR_H

View File

@@ -1,74 +1,74 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_HASH_H
#define TNT_UTILS_HASH_H
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
namespace hash {
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
uint32_t h = seed;
size_t i = wordCount;
do {
uint32_t k = *key++;
k *= 0xcc9e2d51u;
k = (k << 15u) | (k >> 17u);
k *= 0x1b873593u;
h ^= k;
h = (h << 13u) | (h >> 19u);
h = (h * 5u) + 0xe6546b64u;
} while (--i);
h ^= wordCount;
h ^= h >> 16u;
h *= 0x85ebca6bu;
h ^= h >> 13u;
h *= 0xc2b2ae35u;
h ^= h >> 16u;
return h;
}
template<typename T>
struct MurmurHashFn {
uint32_t operator()(const T& key) const noexcept {
static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4.");
return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0);
}
};
// combines two hashes together
template<class T>
inline void combine(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
}
// combines two hashes together, faster but less good
template<class T>
inline void combine_fast(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) << 1u;
}
} // namespace hash
} // namespace utils
#endif // TNT_UTILS_HASH_H
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_HASH_H
#define TNT_UTILS_HASH_H
#include <functional> // for std::hash
#include <stdint.h>
#include <stddef.h>
namespace utils {
namespace hash {
inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept {
uint32_t h = seed;
size_t i = wordCount;
do {
uint32_t k = *key++;
k *= 0xcc9e2d51u;
k = (k << 15u) | (k >> 17u);
k *= 0x1b873593u;
h ^= k;
h = (h << 13u) | (h >> 19u);
h = (h * 5u) + 0xe6546b64u;
} while (--i);
h ^= wordCount;
h ^= h >> 16u;
h *= 0x85ebca6bu;
h ^= h >> 13u;
h *= 0xc2b2ae35u;
h ^= h >> 16u;
return h;
}
template<typename T>
struct MurmurHashFn {
uint32_t operator()(const T& key) const noexcept {
static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4.");
return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0);
}
};
// combines two hashes together
template<class T>
inline void combine(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u);
}
// combines two hashes together, faster but less good
template<class T>
inline void combine_fast(size_t& seed, const T& v) noexcept {
std::hash<T> hasher;
seed ^= hasher(v) << 1u;
}
} // namespace hash
} // namespace utils
#endif // TNT_UTILS_HASH_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,46 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_LOG_H
#define TNT_UTILS_LOG_H
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
struct UTILS_PUBLIC Loggers {
// DEBUG level logging stream
io::ostream& d;
// ERROR level logging stream
io::ostream& e;
// WARNING level logging stream
io::ostream& w;
// INFORMATION level logging stream
io::ostream& i;
};
extern UTILS_PUBLIC Loggers const slog;
} // namespace utils
#endif // TNT_UTILS_LOG_H
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_LOG_H
#define TNT_UTILS_LOG_H
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
struct UTILS_PUBLIC Loggers {
// DEBUG level logging stream
io::ostream& d;
// ERROR level logging stream
io::ostream& e;
// WARNING level logging stream
io::ostream& w;
// INFORMATION level logging stream
io::ostream& i;
// VERBOSE level logging stream
io::ostream& v;
};
extern UTILS_PUBLIC Loggers const slog;
} // namespace utils
#endif // TNT_UTILS_LOG_H

View File

@@ -1,26 +1,26 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_MUTEX_H
#define UTILS_MUTEX_H
#if defined(__linux__) && !defined(__SANITIZE_THREAD__)
#include <utils/linux/Mutex.h>
#else
#include <utils/generic/Mutex.h>
#endif
#endif // UTILS_MUTEX_H
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_MUTEX_H
#define TNT_UTILS_MUTEX_H
#if defined(__linux__)
#include <utils/linux/Mutex.h>
#else
#include <utils/generic/Mutex.h>
#endif
#endif // TNT_UTILS_MUTEX_H

View File

@@ -1,133 +1,133 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_NAMECOMPONENTMANAGER_H
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <utils/SingleInstanceComponentManager.h>
#include <functional>
namespace utils {
class EntityManager;
namespace details {
class SafeString {
public:
SafeString() noexcept = default;
explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { }
SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; }
SafeString& operator=(SafeString&& rhs) noexcept {
std::swap(mCStr, rhs.mCStr);
return *this;
}
~SafeString() { free((void*)mCStr); }
const char* c_str() const noexcept { return mCStr; }
private:
char const* mCStr = nullptr;
};
} // namespace details
/**
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
* \brief Allows clients to associate string labels with entities.
*
* To access the name of an existing entity, clients should first use NameComponentManager to get a
* temporary handle called an \em instance. Please note that instances are ephemeral; clients should
* store entities, not instances.
*
* Usage example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto names = new NameComponentManager(EntityManager::get());
* names->addComponent(myEntity);
* names->setName(names->getInstance(myEntity), "Jeanne d'Arc");
* ...
* printf("%s\n", names->getName(names->getInstance(myEntity));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<details::SafeString> {
public:
using Instance = EntityInstance<NameComponentManager>;
/**
* Creates a new name manager associated with the given entity manager.
*
* Note that multiple string labels could be associated with each entity simply by
* creating multiple instances of NameComponentManager.
*/
explicit NameComponentManager(EntityManager& em);
~NameComponentManager();
/**
* Checks if the given entity already has a name component.
*/
using SingleInstanceComponentManager::hasComponent;
/**
* Gets a temporary handle that can be used to access the name.
*
* @return Non-zero handle if the entity has a name component, 0 otherwise.
*/
Instance getInstance(Entity e) const noexcept {
return Instance(SingleInstanceComponentManager::getInstance(e));
}
/*! \cond PRIVATE */
// these are implemented in SingleInstanceComponentManager<>, but we need to
// reimplement them in each manager, to ensure they are generated in an implementation file
// for backward binary compatibility reasons.
size_t getComponentCount() const noexcept;
Entity const* getEntities() const noexcept;
void gc(const EntityManager& em, size_t ratio = 4) noexcept;
/*! \endcond */
/**
* Adds a name component to the given entity if it doesn't already exist.
*/
void addComponent(Entity e);
/**
* Removes the name component to the given entity if it exists.
*/
void removeComponent(Entity e);
/**
* Stores a copy of the given string and associates it with the given instance.
*/
void setName(Instance instance, const char* name) noexcept;
/**
* Retrieves the string associated with the given instance, or nullptr if none exists.
*
* @return pointer to the copy that was made during setName()
*/
const char* getName(Instance instance) const noexcept;
};
} // namespace utils
#endif // TNT_UTILS_NAMECOMPONENTMANAGER_H
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_NAMECOMPONENTMANAGER_H
#define TNT_UTILS_NAMECOMPONENTMANAGER_H
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/EntityInstance.h>
#include <utils/SingleInstanceComponentManager.h>
#include <functional>
namespace utils {
class EntityManager;
namespace details {
class SafeString {
public:
SafeString() noexcept = default;
explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { }
SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; }
SafeString& operator=(SafeString&& rhs) noexcept {
std::swap(mCStr, rhs.mCStr);
return *this;
}
~SafeString() { free((void*)mCStr); }
const char* c_str() const noexcept { return mCStr; }
private:
char const* mCStr = nullptr;
};
} // namespace details
/**
* \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h
* \brief Allows clients to associate string labels with entities.
*
* To access the name of an existing entity, clients should first use NameComponentManager to get a
* temporary handle called an \em instance. Please note that instances are ephemeral; clients should
* store entities, not instances.
*
* Usage example:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* auto names = new NameComponentManager(EntityManager::get());
* names->addComponent(myEntity);
* names->setName(names->getInstance(myEntity), "Jeanne d'Arc");
* ...
* printf("%s\n", names->getName(names->getInstance(myEntity));
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager<details::SafeString> {
public:
using Instance = EntityInstance<NameComponentManager>;
/**
* Creates a new name manager associated with the given entity manager.
*
* Note that multiple string labels could be associated with each entity simply by
* creating multiple instances of NameComponentManager.
*/
explicit NameComponentManager(EntityManager& em);
~NameComponentManager();
/**
* Checks if the given entity already has a name component.
*/
using SingleInstanceComponentManager::hasComponent;
/**
* Gets a temporary handle that can be used to access the name.
*
* @return Non-zero handle if the entity has a name component, 0 otherwise.
*/
Instance getInstance(Entity e) const noexcept {
return Instance(SingleInstanceComponentManager::getInstance(e));
}
/*! \cond PRIVATE */
// these are implemented in SingleInstanceComponentManager<>, but we need to
// reimplement them in each manager, to ensure they are generated in an implementation file
// for backward binary compatibility reasons.
size_t getComponentCount() const noexcept;
Entity const* getEntities() const noexcept;
void gc(const EntityManager& em, size_t ratio = 4) noexcept;
/*! \endcond */
/**
* Adds a name component to the given entity if it doesn't already exist.
*/
void addComponent(Entity e);
/**
* Removes the name component to the given entity if it exists.
*/
void removeComponent(Entity e);
/**
* Stores a copy of the given string and associates it with the given instance.
*/
void setName(Instance instance, const char* name) noexcept;
/**
* Retrieves the string associated with the given instance, or nullptr if none exists.
*
* @return pointer to the copy that was made during setName()
*/
const char* getName(Instance instance) const noexcept;
};
} // namespace utils
#endif // TNT_UTILS_NAMECOMPONENTMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,290 +1,290 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef UTILS_PATH_H_
#define UTILS_PATH_H_
#include <utils/compiler.h>
#include <iosfwd>
#include <string>
#include <vector>
namespace utils {
/**
* An abstract representation of file and directory paths.
*/
class UTILS_PUBLIC Path {
public:
/**
* Creates a new empty path.
*/
Path() = default;
~Path() = default;
/**
* Creates a new path with the specified pathname.
*
* @param pathname a non-null pathname string
*/
Path(const char* pathname);
/**
* Creates a new path with the specified pathname.
*
* @param pathname a pathname string
*/
Path(const std::string& pathname);
/**
* Tests whether the file or directory denoted by this abstract
* pathname exists.
*
* @return true if the file or directory denoted by this
* abstract pathname exists, false otherwise
*/
bool exists() const;
/**
* Tests whether this abstract pathname represents a regular file.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing file,
* false if the path doesn't exist or represents something
* else (directory, symlink, etc.)
*/
bool isFile() const;
/**
* Tests whether this abstract pathname represents a directory.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing directory,
* false if the path doesn't exist or represents a file
*/
bool isDirectory() const;
/**
* Tests whether this path is empty. An empty path does not
* exist.
*
* @return true if the underlying abstract pathname is empty,
* false otherwise
*/
bool isEmpty() const { return m_path.empty(); }
const char* c_str() const { return m_path.c_str(); }
/**
* Replaces the abstract pathname of this object with the
* specified pathname.
*
* @param pathname a pathname string
*/
void setPath(const std::string& pathname) {
m_path = getCanonicalPath(pathname);
}
/**
* @return the canonical pathname this path represents
*/
const std::string& getPath() const { return m_path; }
/**
* Returns the parent of this path as Path.
* @return a new path containing the parent of this path
*/
Path getParent() const;
/**
* Returns ancestor path where "0" is the immediate parent.
* @return a new path containing the ancestor of this path
*/
Path getAncestor(int n) const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getName() const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname without its extension.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getNameWithoutExtension() const;
/**
* Returns the file extension (after the ".") if one is present.
* Returns the empty string if no filename is present or if the
* path is a directory.
*
* @return the file extension (if one is present and
* this is not a directory), else the empty string.
*/
std::string getExtension() const;
/**
* Returns the absolute representation of this path.
* If this path's pathname starts with a leading '/',
* the returned path is equal to this path. Otherwise,
* this path's pathname is concatenated with the current
* working directory and the result is returned.
*
* @return a new path containing the absolute representation
* of this path
*/
Path getAbsolutePath() const;
/**
* @return true if this abstract pathname is not empty
* and starts with a leading '/', false otherwise
*/
bool isAbsolute() const;
/**
* Splits this object's abstract pathname in a vector of file
* and directory name. If the underlying abstract pathname
* starts with a '/', the returned vector's first element
* will be the string "/".
*
* @return a vector of strings, empty if this path is empty
*/
std::vector<std::string> split() const;
/**
* Concatenates the specified path with this path in a new
* path object.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname is returned without
* being concatenated to this object's pathname.
*
* @param path the path to concatenate with
*
* @return the concatenation of the two paths
*/
Path concat(const Path& path) const;
/**
* Concatenates the specified path with this path and
* stores the result in this path.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname replaces this object's
* pathname.
*
* @param path the path to concatenate with
*/
void concatToSelf(const Path& path);
operator std::string const&() const { return m_path; }
Path operator+(const Path& rhs) const { return concat(rhs); }
Path& operator+=(const Path& rhs) {
concatToSelf(rhs);
return *this;
}
bool operator==(const Path& rhs) const { return m_path == rhs.m_path; }
bool operator!=(const Path& rhs) const { return m_path != rhs.m_path; }
bool operator<(const Path& rhs) const { return m_path < rhs.m_path; }
bool operator>(const Path& rhs) const { return m_path > rhs.m_path; }
friend std::ostream& operator<<(std::ostream& os, const Path& path);
/**
* Returns a canonical copy of the specified pathname by removing
* unnecessary path segments such as ".", ".." and "/".
*
* @param pathname a pathname string
*
* @return the canonical representation of the specified pathname
*/
static std::string getCanonicalPath(const std::string& pathname);
/**
* This method is equivalent to calling root.concat(leaf).
*/
static Path concat(const std::string& root, const std::string& leaf);
/**
* @return a path representing the current working directory
*/
static Path getCurrentDirectory();
/**
* @return a path representing the current executable
*/
static Path getCurrentExecutable();
/**
* @return a path representing a directory where temporary files can be stored
*/
static Path getTemporaryDirectory();
/**
* Creates a directory denoted by the given path.
* This is not recursive and doesn't create intermediate directories.
*
* @return True if directory was successfully created.
* When false, errno should have details on actual error.
*/
bool mkdir() const;
/**
* Creates a directory denoted by the given path.
* This is recursive and parent directories will be created if they do not
* exist.
*
* @return True if directory was successfully created or already exists.
* When false, errno should have details on actual error.
*/
bool mkdirRecursive() const;
/**
* Deletes this file.
*
* @return True if file was successfully deleted.
* When false, errno should have details on actual error.
*/
bool unlinkFile();
/**
* Lists the contents of this directory, skipping hidden files.
*
* @return A vector of paths of the contents of the directory. If the path points to a file,
* nonexistent directory, or empty directory, an empty vector is returned.
*/
std::vector<Path> listContents() const;
private:
std::string m_path;
};
} // namespace utils
#endif // UTILS_PATH_H_
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_UTILS_PATH_H
#define TNT_UTILS_PATH_H
#include <utils/compiler.h>
#include <iosfwd>
#include <string>
#include <vector>
namespace utils {
/**
* An abstract representation of file and directory paths.
*/
class UTILS_PUBLIC Path {
public:
/**
* Creates a new empty path.
*/
Path() = default;
~Path() = default;
/**
* Creates a new path with the specified pathname.
*
* @param pathname a non-null pathname string
*/
Path(const char* pathname);
/**
* Creates a new path with the specified pathname.
*
* @param pathname a pathname string
*/
Path(const std::string& pathname);
/**
* Tests whether the file or directory denoted by this abstract
* pathname exists.
*
* @return true if the file or directory denoted by this
* abstract pathname exists, false otherwise
*/
bool exists() const;
/**
* Tests whether this abstract pathname represents a regular file.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing file,
* false if the path doesn't exist or represents something
* else (directory, symlink, etc.)
*/
bool isFile() const;
/**
* Tests whether this abstract pathname represents a directory.
* This method can only return true if the path exists.
*
* @return true if this pathname represents an existing directory,
* false if the path doesn't exist or represents a file
*/
bool isDirectory() const;
/**
* Tests whether this path is empty. An empty path does not
* exist.
*
* @return true if the underlying abstract pathname is empty,
* false otherwise
*/
bool isEmpty() const { return m_path.empty(); }
const char* c_str() const { return m_path.c_str(); }
/**
* Replaces the abstract pathname of this object with the
* specified pathname.
*
* @param pathname a pathname string
*/
void setPath(const std::string& pathname) {
m_path = getCanonicalPath(pathname);
}
/**
* @return the canonical pathname this path represents
*/
const std::string& getPath() const { return m_path; }
/**
* Returns the parent of this path as Path.
* @return a new path containing the parent of this path
*/
Path getParent() const;
/**
* Returns ancestor path where "0" is the immediate parent.
* @return a new path containing the ancestor of this path
*/
Path getAncestor(int n) const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getName() const;
/**
* Returns the name of the file or directory represented by
* this abstract pathname without its extension.
*
* @return the name of the file or directory represented by
* this abstract pathname, or an empty string if
* this path is empty
*/
std::string getNameWithoutExtension() const;
/**
* Returns the file extension (after the ".") if one is present.
* Returns the empty string if no filename is present or if the
* path is a directory.
*
* @return the file extension (if one is present and
* this is not a directory), else the empty string.
*/
std::string getExtension() const;
/**
* Returns the absolute representation of this path.
* If this path's pathname starts with a leading '/',
* the returned path is equal to this path. Otherwise,
* this path's pathname is concatenated with the current
* working directory and the result is returned.
*
* @return a new path containing the absolute representation
* of this path
*/
Path getAbsolutePath() const;
/**
* @return true if this abstract pathname is not empty
* and starts with a leading '/', false otherwise
*/
bool isAbsolute() const;
/**
* Splits this object's abstract pathname in a vector of file
* and directory name. If the underlying abstract pathname
* starts with a '/', the returned vector's first element
* will be the string "/".
*
* @return a vector of strings, empty if this path is empty
*/
std::vector<std::string> split() const;
/**
* Concatenates the specified path with this path in a new
* path object.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname is returned without
* being concatenated to this object's pathname.
*
* @param path the path to concatenate with
*
* @return the concatenation of the two paths
*/
Path concat(const Path& path) const;
/**
* Concatenates the specified path with this path and
* stores the result in this path.
*
* @note if the pathname to concatenate with starts with
* a leading '/' then that pathname replaces this object's
* pathname.
*
* @param path the path to concatenate with
*/
void concatToSelf(const Path& path);
operator std::string const&() const { return m_path; }
Path operator+(const Path& rhs) const { return concat(rhs); }
Path& operator+=(const Path& rhs) {
concatToSelf(rhs);
return *this;
}
bool operator==(const Path& rhs) const { return m_path == rhs.m_path; }
bool operator!=(const Path& rhs) const { return m_path != rhs.m_path; }
bool operator<(const Path& rhs) const { return m_path < rhs.m_path; }
bool operator>(const Path& rhs) const { return m_path > rhs.m_path; }
friend std::ostream& operator<<(std::ostream& os, const Path& path);
/**
* Returns a canonical copy of the specified pathname by removing
* unnecessary path segments such as ".", ".." and "/".
*
* @param pathname a pathname string
*
* @return the canonical representation of the specified pathname
*/
static std::string getCanonicalPath(const std::string& pathname);
/**
* This method is equivalent to calling root.concat(leaf).
*/
static Path concat(const std::string& root, const std::string& leaf);
/**
* @return a path representing the current working directory
*/
static Path getCurrentDirectory();
/**
* @return a path representing the current executable
*/
static Path getCurrentExecutable();
/**
* @return a path representing a directory where temporary files can be stored
*/
static Path getTemporaryDirectory();
/**
* Creates a directory denoted by the given path.
* This is not recursive and doesn't create intermediate directories.
*
* @return True if directory was successfully created.
* When false, errno should have details on actual error.
*/
bool mkdir() const;
/**
* Creates a directory denoted by the given path.
* This is recursive and parent directories will be created if they do not
* exist.
*
* @return True if directory was successfully created or already exists.
* When false, errno should have details on actual error.
*/
bool mkdirRecursive() const;
/**
* Deletes this file.
*
* @return True if file was successfully deleted.
* When false, errno should have details on actual error.
*/
bool unlinkFile();
/**
* Lists the contents of this directory, skipping hidden files.
*
* @return A vector of paths of the contents of the directory. If the path points to a file,
* nonexistent directory, or empty directory, an empty vector is returned.
*/
std::vector<Path> listContents() const;
private:
std::string m_path;
};
} // namespace utils
#endif // TNT_UTILS_PATH_H

Some files were not shown because too many files have changed in this diff Show More