diff --git a/bullet3 b/bullet3 index 6bb8d112..63c4d67e 160000 --- a/bullet3 +++ b/bullet3 @@ -1 +1 @@ -Subproject commit 6bb8d1123d8a55d407b19fd3357c724d0f5c9d3c +Subproject commit 63c4d67e337017f9d8b298c900e9aabdb69296e7 diff --git a/examples/AppExampleFabric/scripts/compile-materials.sh b/examples/AppExampleFabric/scripts/compile-materials.sh index 5fcc62ef..b0946681 100755 --- a/examples/AppExampleFabric/scripts/compile-materials.sh +++ b/examples/AppExampleFabric/scripts/compile-materials.sh @@ -3,7 +3,7 @@ set -e # Make sure the filament matc tool is available: -matc_path=../../../filament/out/cmake-release/tools/matc/matc +matc_path=../../filament/out/cmake-release/tools/matc/matc if [ ! -f "$matc_path" ]; then echo "The filament matc tool is missing. Did you build Filament properly?" exit 1 diff --git a/examples/Shared/assets/background_image.filamat b/examples/Shared/assets/background_image.filamat index cfaaafec..35c2bbae 100644 Binary files a/examples/Shared/assets/background_image.filamat and b/examples/Shared/assets/background_image.filamat differ diff --git a/examples/Shared/assets/transparent_shadow_material.filamat b/examples/Shared/assets/transparent_shadow_material.filamat index d7d953f2..96a8110c 100644 Binary files a/examples/Shared/assets/transparent_shadow_material.filamat and b/examples/Shared/assets/transparent_shadow_material.filamat differ diff --git a/filament b/filament index 3ca9b9cd..85ebd67a 160000 --- a/filament +++ b/filament @@ -1 +1 @@ -Subproject commit 3ca9b9cd0fdb6435af3a084813edd6ead8abc2b8 +Subproject commit 85ebd67a282433bde6a071f72bc048d94ba031d4 diff --git a/package/android/CMakeLists.txt b/package/android/CMakeLists.txt index d39d619c..a7e1ec71 100644 --- a/package/android/CMakeLists.txt +++ b/package/android/CMakeLists.txt @@ -112,6 +112,8 @@ target_include_directories( "${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/react/turbomodule" # <-- CallInvokerHolder JNI wrapper "${FILAMENT_DIR}/include" "${BULLET3_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}/../../filament/filament/backend/include" # <-- Filament backend private headers (dev) + "${FILAMENT_DIR}/include/private" # <-- Filament private headers (distributed package) ) # Link everything together diff --git a/package/android/gradle.properties b/package/android/gradle.properties index da4a8e42..e150252c 100644 --- a/package/android/gradle.properties +++ b/package/android/gradle.properties @@ -1,5 +1,5 @@ Filament_kotlinVersion=1.7.0 -Filament_minSdkVersion=21 -Filament_targetSdkVersion=31 -Filament_compileSdkVersion=31 -Filament_ndkversion=25.1.8937393 +Filament_minSdkVersion=24 +Filament_targetSdkVersion=35 +Filament_compileSdkVersion=35 +Filament_ndkversion=27.1.12297006 diff --git a/package/android/libs/filament/README.md b/package/android/libs/filament/README.md index 79ec596b..abe815eb 100644 --- a/package/android/libs/filament/README.md +++ b/package/android/libs/filament/README.md @@ -92,14 +92,14 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory. ### Linux ```make -FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl +FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil CC=clang++ main: main.o $(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) -lpthread -lc++ -ldl -o main main.o: main.cpp - $(CC) -Iinclude/ -std=c++17 -pthread -c main.cpp + $(CC) -Iinclude/ -std=c++20 -pthread -c main.cpp clean: rm -f main main.o @@ -110,15 +110,16 @@ clean: ### macOS ```make -FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl +FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo CC=clang++ +ARCH ?= $(shell uname -m) main: main.o - $(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main + $(CC) -Llib/$(ARCH)/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main main.o: main.cpp - $(CC) -Iinclude/ -std=c++17 -c main.cpp + $(CC) -Iinclude/ -std=c++20 -c main.cpp clean: rm -f main main.o @@ -139,7 +140,7 @@ used to change the run-time library version. ```make FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \ - utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib + utils.lib geometry.lib smol-v.lib ibl.lib abseil.lib CC=cl.exe main.exe: main.obj @@ -147,7 +148,7 @@ main.exe: main.obj gdi32.lib user32.lib opengl32.lib main.obj: main.cpp - $(CC) /MT /Iinclude\\ /std:c++17 /c main.cpp + $(CC) /MT /Iinclude\\ /std:c++20 /c main.cpp clean: del main.exe main.obj diff --git a/package/android/libs/filament/include/backend/AcquiredImage.h b/package/android/libs/filament/include/backend/AcquiredImage.h index fec27a53..ff0840e5 100644 --- a/package/android/libs/filament/include/backend/AcquiredImage.h +++ b/package/android/libs/filament/include/backend/AcquiredImage.h @@ -18,6 +18,7 @@ #define TNT_FILAMENT_BACKEND_PRIVATE_ACQUIREDIMAGE_H #include +#include namespace filament::backend { diff --git a/package/android/libs/filament/include/backend/BufferDescriptor.h b/package/android/libs/filament/include/backend/BufferDescriptor.h index ebb57537..afc0da20 100644 --- a/package/android/libs/filament/include/backend/BufferDescriptor.h +++ b/package/android/libs/filament/include/backend/BufferDescriptor.h @@ -20,10 +20,15 @@ #define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H #include -#include + +#include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { class CallbackHandler; @@ -89,8 +94,8 @@ class UTILS_PUBLIC BufferDescriptor { * @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, - Callback callback = nullptr, void* user = nullptr) noexcept + BufferDescriptor(void const* buffer, size_t const size, + Callback const callback = nullptr, void* user = nullptr) noexcept : buffer(const_cast(buffer)), size(size), mCallback(callback), mUser(user) { } @@ -98,11 +103,12 @@ class UTILS_PUBLIC BufferDescriptor { * 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 handler A custom handler for the callback * @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 + BufferDescriptor(void const* buffer, size_t const size, + CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept : buffer(const_cast(buffer)), size(size), mCallback(callback), mUser(user), mHandler(handler) { } @@ -116,8 +122,9 @@ class UTILS_PUBLIC BufferDescriptor { * * @param buffer Memory address of the CPU buffer to reference * @param size Size of the CPU buffer in bytes + * @param data A pointer to the data * @param handler Handler to use to dispatch the callback, or nullptr for the default handler - * @return a new BufferDescriptor + * @return A new BufferDescriptor */ template static BufferDescriptor make(void const* buffer, size_t size, T* data, @@ -164,7 +171,7 @@ class UTILS_PUBLIC BufferDescriptor { * @param callback The new callback function * @param user An opaque user pointer passed to the callbeck function when it's called */ - void setCallback(Callback callback, void* user = nullptr) noexcept { + void setCallback(Callback const callback, void* user = nullptr) noexcept { this->mCallback = callback; this->mUser = user; this->mHandler = nullptr; @@ -176,7 +183,7 @@ class UTILS_PUBLIC BufferDescriptor { * @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 { + void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept { mCallback = callback; mUser = user; mHandler = handler; diff --git a/package/android/libs/filament/include/backend/CallbackHandler.h b/package/android/libs/filament/include/backend/CallbackHandler.h index 036031a9..9700186d 100644 --- a/package/android/libs/filament/include/backend/CallbackHandler.h +++ b/package/android/libs/filament/include/backend/CallbackHandler.h @@ -64,7 +64,7 @@ class CallbackHandler { virtual void post(void* user, Callback callback) = 0; protected: - virtual ~CallbackHandler() = default; + virtual ~CallbackHandler(); }; } // namespace filament::backend diff --git a/package/android/libs/filament/include/backend/DescriptorSetOffsetArray.h b/package/android/libs/filament/include/backend/DescriptorSetOffsetArray.h new file mode 100644 index 00000000..d7cfb763 --- /dev/null +++ b/package/android/libs/filament/include/backend/DescriptorSetOffsetArray.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 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_COMMANDSTREAMVECTOR_H +#define TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H + +#include + +#include +#include + +#include +#include + + +namespace utils::io { +class ostream; +} // namespace utils::io + +namespace filament::backend { + +void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept; + +class DescriptorSetOffsetArray { +public: + using value_type = uint32_t; + using reference = value_type&; + using const_reference = value_type const&; + using size_type = uint32_t; + using difference_type = int32_t; + using pointer = value_type*; + using const_pointer = value_type const*; + using iterator = pointer; + using const_iterator = const_pointer; + + DescriptorSetOffsetArray() noexcept = default; + + ~DescriptorSetOffsetArray() noexcept = default; + + DescriptorSetOffsetArray(size_type size, DriverApi& driver) noexcept { + mOffsets = (value_type *)allocateFromCommandStream(driver, + size * sizeof(value_type), alignof(value_type)); + std::uninitialized_fill_n(mOffsets, size, 0); + } + + DescriptorSetOffsetArray(std::initializer_list list, DriverApi& driver) noexcept { + mOffsets = (value_type *)allocateFromCommandStream(driver, + list.size() * sizeof(value_type), alignof(value_type)); + std::uninitialized_copy(list.begin(), list.end(), mOffsets); + } + + DescriptorSetOffsetArray(DescriptorSetOffsetArray const&) = delete; + DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray const&) = delete; + + DescriptorSetOffsetArray(DescriptorSetOffsetArray&& rhs) noexcept + : mOffsets(rhs.mOffsets) { + rhs.mOffsets = nullptr; + } + + DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray&& rhs) noexcept { + if (this != &rhs) { + mOffsets = rhs.mOffsets; + rhs.mOffsets = nullptr; + } + return *this; + } + + bool empty() const noexcept { return mOffsets == nullptr; } + + value_type* data() noexcept { return mOffsets; } + const value_type* data() const noexcept { return mOffsets; } + + + reference operator[](size_type n) noexcept { + return *(data() + n); + } + + const_reference operator[](size_type n) const noexcept { + return *(data() + n); + } + + void clear() noexcept { + mOffsets = nullptr; + } + +private: + value_type *mOffsets = nullptr; +}; + +} // namespace filament::backend + +#if !defined(NDEBUG) +utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::DescriptorSetOffsetArray& rhs); +#endif + +#endif //TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H diff --git a/package/android/libs/filament/include/backend/DriverEnums.h b/package/android/libs/filament/include/backend/DriverEnums.h index ef1c655c..3b1ae238 100644 --- a/package/android/libs/filament/include/backend/DriverEnums.h +++ b/package/android/libs/filament/include/backend/DriverEnums.h @@ -19,21 +19,32 @@ #ifndef TNT_FILAMENT_BACKEND_DRIVERENUMS_H #define TNT_FILAMENT_BACKEND_DRIVERENUMS_H -#include #include // Because we define ERROR in the FenceStatus enum. +#include #include -#include +#include +#include +#include +#include +#include +#include #include -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers +#include +#include +#include +#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + /** * Types and enums used by filament's driver. * @@ -90,11 +101,26 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON */ static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40; +/** + * Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the + * given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is + * true. + * + * This is only supported by EGL(Android). Other GL platforms (GLX, WGL, etc) don't support it + * because the swapchain MSAA settings must be configured before window creation. + */ +static constexpr uint64_t SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES = 0x80; static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES. static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3. static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects. static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES. +static constexpr size_t MAX_DESCRIPTOR_SET_COUNT = 4; // This is guaranteed by Vulkan. +static constexpr size_t MAX_DESCRIPTOR_COUNT = 64; // per set + +static constexpr size_t MAX_PUSH_CONSTANT_COUNT = 32; // Vulkan 1.1 spec allows for 128-byte + // of push constant (we assume 4-byte + // types). // Per feature level caps // Use (int)FeatureLevel to index this array @@ -103,8 +129,8 @@ static constexpr struct { const size_t MAX_FRAGMENT_SAMPLER_COUNT; } FEATURE_LEVEL_CAPS[4] = { { 0, 0 }, // do not use - { 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal - { 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal + { 16, 16 }, // guaranteed by OpenGL ES, Vulkan, Metal And WebGPU + { 16, 16 }, // guaranteed by OpenGL ES, Vulkan, Metal And WebGPU { 31, 31 }, // guaranteed by Metal }; @@ -112,9 +138,13 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT, "The number of buffer objects that can be attached to a VertexBuffer must be " "less than or equal to the maximum number of vertex attributes."); -static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES. +static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES. static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES. +static constexpr uint8_t EXTERNAL_SAMPLER_DATA_INDEX_UNUSED = + uint8_t(-1);// Case where the descriptor set binding isnt using any external sampler state + // and therefore doesn't have a valid entry. + /** * Defines the backend's feature levels. */ @@ -133,7 +163,8 @@ enum class Backend : uint8_t { OPENGL = 1, //!< Selects the OpenGL/ES driver (default on Android) VULKAN = 2, //!< Selects the Vulkan driver if the platform supports it (default on Linux/Windows) METAL = 3, //!< Selects the Metal driver if the platform supports it (default on MacOS/iOS). - NOOP = 4, //!< Selects the no-op driver for testing purposes. + WEBGPU = 4, //!< Selects the Webgpu driver if the platform supports webgpu. + NOOP = 5, //!< Selects the no-op driver for testing purposes. }; enum class TimerQueryResult : int8_t { @@ -142,7 +173,7 @@ enum class TimerQueryResult : int8_t { AVAILABLE = 1, // result is available }; -static constexpr const char* backendToString(Backend backend) { +constexpr std::string_view to_string(Backend const backend) noexcept { switch (backend) { case Backend::NOOP: return "Noop"; @@ -152,9 +183,12 @@ static constexpr const char* backendToString(Backend backend) { return "Vulkan"; case Backend::METAL: return "Metal"; - default: - return "Unknown"; + case Backend::WEBGPU: + return "WebGPU"; + case Backend::DEFAULT: + return "Default"; } + return "Unknown"; } /** @@ -163,14 +197,16 @@ static constexpr const char* backendToString(Backend backend) { * - The Metal backend can prefer precompiled Metal libraries, while falling back to MSL. */ enum class ShaderLanguage { + UNSPECIFIED = -1, ESSL1 = 0, ESSL3 = 1, SPIRV = 2, MSL = 3, METAL_LIBRARY = 4, + WGSL = 5, }; -static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) { +constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noexcept { switch (shaderLanguage) { case ShaderLanguage::ESSL1: return "ESSL 1.0"; @@ -182,9 +218,310 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag return "MSL"; case ShaderLanguage::METAL_LIBRARY: return "Metal precompiled library"; + case ShaderLanguage::WGSL: + return "WGSL"; + case ShaderLanguage::UNSPECIFIED: + return "Unspecified"; } + return "UNKNOWN"; } +enum class ShaderStage : uint8_t { + VERTEX = 0, + FRAGMENT = 1, + COMPUTE = 2 +}; + +static constexpr size_t PIPELINE_STAGE_COUNT = 2; +enum class ShaderStageFlags : uint8_t { + NONE = 0, + VERTEX = 0x1, + FRAGMENT = 0x2, + COMPUTE = 0x4, + ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE +}; + +constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { + switch (type) { + case ShaderStage::VERTEX: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); + case ShaderStage::FRAGMENT: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT)); + case ShaderStage::COMPUTE: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE)); + } +} + +enum class TextureType : uint8_t { + FLOAT, + INT, + UINT, + DEPTH, + STENCIL, + DEPTH_STENCIL +}; + +constexpr std::string_view to_string(TextureType type) noexcept { + switch (type) { + case TextureType::FLOAT: return "FLOAT"; + case TextureType::INT: return "INT"; + case TextureType::UINT: return "UINT"; + case TextureType::DEPTH: return "DEPTH"; + case TextureType::STENCIL: return "STENCIL"; + case TextureType::DEPTH_STENCIL: return "DEPTH_STENCIL"; + } + return "UNKNOWN"; +} + + enum class DescriptorType : uint8_t { + SAMPLER_2D_FLOAT, + SAMPLER_2D_INT, + SAMPLER_2D_UINT, + SAMPLER_2D_DEPTH, + + SAMPLER_2D_ARRAY_FLOAT, + SAMPLER_2D_ARRAY_INT, + SAMPLER_2D_ARRAY_UINT, + SAMPLER_2D_ARRAY_DEPTH, + + SAMPLER_CUBE_FLOAT, + SAMPLER_CUBE_INT, + SAMPLER_CUBE_UINT, + SAMPLER_CUBE_DEPTH, + + SAMPLER_CUBE_ARRAY_FLOAT, + SAMPLER_CUBE_ARRAY_INT, + SAMPLER_CUBE_ARRAY_UINT, + SAMPLER_CUBE_ARRAY_DEPTH, + + SAMPLER_3D_FLOAT, + SAMPLER_3D_INT, + SAMPLER_3D_UINT, + + SAMPLER_2D_MS_FLOAT, + SAMPLER_2D_MS_INT, + SAMPLER_2D_MS_UINT, + + SAMPLER_2D_MS_ARRAY_FLOAT, + SAMPLER_2D_MS_ARRAY_INT, + SAMPLER_2D_MS_ARRAY_UINT, + + SAMPLER_EXTERNAL, + UNIFORM_BUFFER, + SHADER_STORAGE_BUFFER, + INPUT_ATTACHMENT, + }; + +constexpr bool isDepthDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_DEPTH: + case DescriptorType::SAMPLER_2D_ARRAY_DEPTH: + case DescriptorType::SAMPLER_CUBE_DEPTH: + case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH: + return true; + default: ; + } + return false; +} + +constexpr bool isFloatDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_FLOAT: + case DescriptorType::SAMPLER_2D_ARRAY_FLOAT: + case DescriptorType::SAMPLER_CUBE_FLOAT: + case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT: + case DescriptorType::SAMPLER_3D_FLOAT: + case DescriptorType::SAMPLER_2D_MS_FLOAT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT: + return true; + default: ; + } + return false; +} + +constexpr bool isIntDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_INT: + case DescriptorType::SAMPLER_2D_ARRAY_INT: + case DescriptorType::SAMPLER_CUBE_INT: + case DescriptorType::SAMPLER_CUBE_ARRAY_INT: + case DescriptorType::SAMPLER_3D_INT: + case DescriptorType::SAMPLER_2D_MS_INT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_INT: + return true; + default: ; + } + return false; +} + +constexpr bool isUnsignedIntDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_UINT: + case DescriptorType::SAMPLER_2D_ARRAY_UINT: + case DescriptorType::SAMPLER_CUBE_UINT: + case DescriptorType::SAMPLER_CUBE_ARRAY_UINT: + case DescriptorType::SAMPLER_3D_UINT: + case DescriptorType::SAMPLER_2D_MS_UINT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool is3dTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_3D_FLOAT: + case DescriptorType::SAMPLER_3D_INT: + case DescriptorType::SAMPLER_3D_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool is2dTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_FLOAT: + case DescriptorType::SAMPLER_2D_INT: + case DescriptorType::SAMPLER_2D_UINT: + case DescriptorType::SAMPLER_2D_DEPTH: + case DescriptorType::SAMPLER_2D_MS_FLOAT: + case DescriptorType::SAMPLER_2D_MS_INT: + case DescriptorType::SAMPLER_2D_MS_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool is2dArrayTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_ARRAY_FLOAT: + case DescriptorType::SAMPLER_2D_ARRAY_INT: + case DescriptorType::SAMPLER_2D_ARRAY_UINT: + case DescriptorType::SAMPLER_2D_ARRAY_DEPTH: + case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_INT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool isCubeTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_CUBE_FLOAT: + case DescriptorType::SAMPLER_CUBE_INT: + case DescriptorType::SAMPLER_CUBE_UINT: + case DescriptorType::SAMPLER_CUBE_DEPTH: + return true; + default: ; + } + return false; +} + +constexpr bool isCubeArrayTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT: + case DescriptorType::SAMPLER_CUBE_ARRAY_INT: + case DescriptorType::SAMPLER_CUBE_ARRAY_UINT: + case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH: + return true; + default: ; + } + return false; +} + +constexpr bool isMultiSampledTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_MS_FLOAT: + case DescriptorType::SAMPLER_2D_MS_INT: + case DescriptorType::SAMPLER_2D_MS_UINT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_INT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: + return true; + default: ; + } + return false; +} + +constexpr std::string_view to_string(DescriptorType type) noexcept { + #define DESCRIPTOR_TYPE_CASE(TYPE) case DescriptorType::TYPE: return #TYPE; + switch (type) { + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_3D_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_3D_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_3D_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_EXTERNAL) + DESCRIPTOR_TYPE_CASE(UNIFORM_BUFFER) + DESCRIPTOR_TYPE_CASE(SHADER_STORAGE_BUFFER) + DESCRIPTOR_TYPE_CASE(INPUT_ATTACHMENT) + } + return "UNKNOWN"; + #undef DESCRIPTOR_TYPE_CASE +} + +enum class DescriptorFlags : uint8_t { + NONE = 0x00, + + // Indicate a UNIFORM_BUFFER will have dynamic offsets. + DYNAMIC_OFFSET = 0x01, + + // To indicate a texture/sampler type should be unfiltered. + UNFILTERABLE = 0x02, +}; + +using descriptor_set_t = uint8_t; + +using descriptor_binding_t = uint8_t; + +struct DescriptorSetLayoutDescriptor { + static bool isSampler(DescriptorType type) noexcept { + return int(type) <= int(DescriptorType::SAMPLER_EXTERNAL); + } + static bool isBuffer(DescriptorType type) noexcept { + return type == DescriptorType::UNIFORM_BUFFER || + type == DescriptorType::SHADER_STORAGE_BUFFER; + } + DescriptorType type; + ShaderStageFlags stageFlags; + descriptor_binding_t binding; + DescriptorFlags flags = DescriptorFlags::NONE; + uint16_t count = 0; + + friend bool operator==(DescriptorSetLayoutDescriptor const& lhs, + DescriptorSetLayoutDescriptor const& rhs) noexcept { + return lhs.type == rhs.type && + lhs.flags == rhs.flags && + lhs.count == rhs.count && + lhs.stageFlags == rhs.stageFlags; + } +}; + /** * Bitmask for selecting render buffers */ @@ -207,7 +544,7 @@ enum class TargetBufferFlags : uint32_t { ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected. }; -inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept { +constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept { if (index == 0u) return TargetBufferFlags::COLOR0; if (index == 1u) return TargetBufferFlags::COLOR1; if (index == 2u) return TargetBufferFlags::COLOR2; @@ -222,12 +559,21 @@ inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept } /** - * Frequency at which a buffer is expected to be modified and used. This is used as an hint - * for the driver to make better decisions about managing memory internally. + * How the buffer will be used. */ enum class BufferUsage : uint8_t { - STATIC, //!< content modified once, used many times - DYNAMIC, //!< content modified frequently, used many times + STATIC = 0, //!< (legacy) content modified once, used many times + DYNAMIC = 1, //!< (legacy) content modified frequently, used many times + DYNAMIC_BIT = 0x1, //!< buffer can be modified frequently, used many times + SHARED_WRITE_BIT = 0x04, //!< buffer can be memory mapped for write operations +}; + +/** + * How the buffer will be mapped. + */ +enum class MapBufferAccessFlags : uint8_t { + WRITE_BIT = 0x2, //!< buffer is mapped from writing + INVALIDATE_RANGE_BIT = 0x4, //!< the mapped range content is lost }; /** @@ -243,8 +589,19 @@ struct Viewport { int32_t right() const noexcept { return left + int32_t(width); } //! get the top coordinate in window space of the viewport int32_t top() const noexcept { return bottom + int32_t(height); } -}; + friend bool operator==(Viewport const& lhs, Viewport const& rhs) noexcept { + // clang can do this branchless with xor/or + return lhs.left == rhs.left && lhs.bottom == rhs.bottom && + lhs.width == rhs.width && lhs.height == rhs.height; + } + + friend bool operator!=(Viewport const& lhs, Viewport const& rhs) noexcept { + // clang is being dumb and uses branches + return bool(((lhs.left ^ rhs.left) | (lhs.bottom ^ rhs.bottom)) | + ((lhs.width ^ rhs.width) | (lhs.height ^ rhs.height))); + } +}; /** * Specifies the mapping of the near and far clipping plane to window coordinates. @@ -264,15 +621,6 @@ enum class FenceStatus : int8_t { TIMEOUT_EXPIRED = 1, //!< wait()'s timeout expired. The Fence condition is not satisfied. }; -/** - * Status codes for sync objects - */ -enum class SyncStatus : int8_t { - ERROR = -1, //!< An error occurred. The Sync is not signaled. - SIGNALED = 0, //!< The Sync is signaled. - NOT_SIGNALED = 1, //!< The Sync is not signaled yet -}; - static constexpr uint64_t FENCE_WAIT_FOR_EVER = uint64_t(-1); /** @@ -291,6 +639,15 @@ enum class ShaderModel : uint8_t { }; static constexpr size_t SHADER_MODEL_COUNT = 2; +constexpr std::string_view to_string(ShaderModel model) noexcept { + switch (model) { + case ShaderModel::MOBILE: + return "mobile"; + case ShaderModel::DESKTOP: + return "desktop"; + } +} + /** * Primitive types */ @@ -303,6 +660,18 @@ enum class PrimitiveType : uint8_t { TRIANGLE_STRIP = 5 //!< triangle strip }; +[[nodiscard]] constexpr bool isStripPrimitiveType(const PrimitiveType type) { + switch (type) { + case PrimitiveType::POINTS: + case PrimitiveType::LINES: + case PrimitiveType::TRIANGLES: + return false; + case PrimitiveType::LINE_STRIP: + case PrimitiveType::TRIANGLE_STRIP: + return true; + } +} + /** * Supported uniform types */ @@ -331,7 +700,7 @@ enum class UniformType : uint8_t { /** * Supported constant parameter types */ - enum class ConstantType : uint8_t { +enum class ConstantType : uint8_t { INT, FLOAT, BOOL @@ -344,11 +713,29 @@ enum class Precision : uint8_t { DEFAULT }; +union ConstantValue { + int32_t i; + float f; + bool b; +}; + /** * Shader compiler priority queue + * + * On platforms which support parallel shader compilation, compilation requests will be processed in + * order of priority, then insertion order. See Material::compile(). */ enum class CompilerPriorityQueue : uint8_t { + /** We need this program NOW. + * + * When passed as an argument to Material::compile(), if the platform doesn't support parallel + * compilation, but does support amortized shader compilation, the given shader program will be + * synchronously compiled. + */ + CRITICAL, + /** We will need this program soon. */ HIGH, + /** We will need this program eventually. */ LOW }; @@ -362,6 +749,24 @@ enum class SamplerType : uint8_t { SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2) }; +constexpr std::string_view to_string(SamplerType const type) noexcept { + switch (type) { + case SamplerType::SAMPLER_2D: + return "SAMPLER_2D"; + case SamplerType::SAMPLER_2D_ARRAY: + return "SAMPLER_2D_ARRAY"; + case SamplerType::SAMPLER_CUBEMAP: + return "SAMPLER_CUBEMAP"; + case SamplerType::SAMPLER_EXTERNAL: + return "SAMPLER_EXTERNAL"; + case SamplerType::SAMPLER_3D: + return "SAMPLER_3D"; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: + return "SAMPLER_CUBEMAP_ARRAY"; + } + return "Unknown"; +} + //! Subpass type enum class SubpassType : uint8_t { SUBPASS_INPUT @@ -375,6 +780,20 @@ enum class SamplerFormat : uint8_t { SHADOW = 3 //!< shadow sampler (PCF) }; +constexpr std::string_view to_string(SamplerFormat const format) noexcept { + switch (format) { + case SamplerFormat::INT: + return "INT"; + case SamplerFormat::UINT: + return "UINT"; + case SamplerFormat::FLOAT: + return "FLOAT"; + case SamplerFormat::SHADOW: + return "SHADOW"; + } + return "Unknown"; +} + /** * Supported element types */ @@ -414,6 +833,15 @@ enum class BufferObjectBinding : uint8_t { SHADER_STORAGE }; +constexpr std::string_view to_string(BufferObjectBinding type) noexcept { + switch (type) { + case BufferObjectBinding::VERTEX: return "VERTEX"; + case BufferObjectBinding::UNIFORM: return "UNIFORM"; + case BufferObjectBinding::SHADER_STORAGE: return "SHADER_STORAGE"; + } + return "UNKNOWN"; +} + //! Face culling Mode enum class CullingMode : uint8_t { NONE, //!< No culling, front and back faces are visible @@ -675,6 +1103,8 @@ enum class TextureFormat : uint16_t { SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB }; +TextureType getTextureType(TextureFormat format) noexcept; + //! Bitmask describing the intended Texture Usage enum class TextureUsage : uint16_t { NONE = 0x0000, @@ -686,8 +1116,10 @@ enum class TextureUsage : uint16_t { SUBPASS_INPUT = 0x0020, //!< Texture can be used as a subpass input BLIT_SRC = 0x0040, //!< Texture can be used the source of a blit() BLIT_DST = 0x0080, //!< Texture can be used the destination of a blit() - PROTECTED = 0x0100, //!< Texture can be used the destination of a blit() - DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage + PROTECTED = 0x0100, //!< Texture can be used for protected content + GEN_MIPMAPPABLE = 0x0200, //!< Texture can be used with generateMipmaps() + DEFAULT = UPLOADABLE | SAMPLEABLE, //!< Default texture usage + ALL_ATTACHMENTS = COLOR_ATTACHMENT | DEPTH_ATTACHMENT | STENCIL_ATTACHMENT | SUBPASS_INPUT, //!< Mask of all attachments }; //! Texture swizzle @@ -701,7 +1133,7 @@ enum class TextureSwizzle : uint8_t { }; //! returns whether this format a depth format -static constexpr bool isDepthFormat(TextureFormat format) noexcept { +constexpr bool isDepthFormat(TextureFormat format) noexcept { switch (format) { case TextureFormat::DEPTH32F: case TextureFormat::DEPTH24: @@ -714,7 +1146,7 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept { } } -static constexpr bool isStencilFormat(TextureFormat format) noexcept { +constexpr bool isStencilFormat(TextureFormat format) noexcept { switch (format) { case TextureFormat::STENCIL8: case TextureFormat::DEPTH24_STENCIL8: @@ -725,7 +1157,34 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept { } } -static constexpr bool isUnsignedIntFormat(TextureFormat format) { +constexpr bool isColorFormat(TextureFormat format) noexcept { + switch (format) { + // Standard color formats + case TextureFormat::R8: + case TextureFormat::RG8: + case TextureFormat::RGBA8: + case TextureFormat::R16F: + case TextureFormat::RG16F: + case TextureFormat::RGBA16F: + case TextureFormat::R32F: + case TextureFormat::RG32F: + case TextureFormat::RGBA32F: + case TextureFormat::RGB10_A2: + case TextureFormat::R11F_G11F_B10F: + case TextureFormat::SRGB8: + case TextureFormat::SRGB8_A8: + case TextureFormat::RGB8: + case TextureFormat::RGB565: + case TextureFormat::RGB5_A1: + case TextureFormat::RGBA4: + return true; + default: + break; + } + return false; +} + +constexpr bool isUnsignedIntFormat(TextureFormat format) { switch (format) { case TextureFormat::R8UI: case TextureFormat::R16UI: @@ -746,7 +1205,7 @@ static constexpr bool isUnsignedIntFormat(TextureFormat format) { } } -static constexpr bool isSignedIntFormat(TextureFormat format) { +constexpr bool isSignedIntFormat(TextureFormat format) { switch (format) { case TextureFormat::R8I: case TextureFormat::R16I: @@ -768,35 +1227,35 @@ static constexpr bool isSignedIntFormat(TextureFormat format) { } //! returns whether this format is a compressed format -static constexpr bool isCompressedFormat(TextureFormat format) noexcept { +constexpr bool isCompressedFormat(TextureFormat format) noexcept { return format >= TextureFormat::EAC_R11; } //! returns whether this format is an ETC2 compressed format -static constexpr bool isETC2Compression(TextureFormat format) noexcept { +constexpr bool isETC2Compression(TextureFormat format) noexcept { return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8; } //! returns whether this format is an S3TC compressed format -static constexpr bool isS3TCCompression(TextureFormat format) noexcept { +constexpr bool isS3TCCompression(TextureFormat format) noexcept { return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA; } -static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept { +constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept { return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA; } //! returns whether this format is an RGTC compressed format -static constexpr bool isRGTCCompression(TextureFormat format) noexcept { +constexpr bool isRGTCCompression(TextureFormat format) noexcept { return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2; } //! returns whether this format is an BPTC compressed format -static constexpr bool isBPTCCompression(TextureFormat format) noexcept { +constexpr bool isBPTCCompression(TextureFormat format) noexcept { return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM; } -static constexpr bool isASTCCompression(TextureFormat format) noexcept { +constexpr bool isASTCCompression(TextureFormat format) noexcept { return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12; } @@ -857,7 +1316,7 @@ enum class SamplerCompareFunc : uint8_t { }; //! Sampler parameters -struct SamplerParams { // NOLINT +struct SamplerParams { // NOLINT SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST) SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST) SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE) @@ -881,6 +1340,9 @@ struct SamplerParams { // NOLINT struct EqualTo { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { + assert_invariant(lhs.padding0 == 0); + assert_invariant(lhs.padding1 == 0); + assert_invariant(lhs.padding2 == 0); auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); return *pLhs == *pRhs; @@ -889,17 +1351,31 @@ struct SamplerParams { // NOLINT struct LessThan { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { + assert_invariant(lhs.padding0 == 0); + assert_invariant(lhs.padding1 == 0); + assert_invariant(lhs.padding2 == 0); auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); - return *pLhs == *pRhs; + return *pLhs < *pRhs; } }; + bool isFiltered() const noexcept { + return filterMag != SamplerMagFilter::NEAREST || filterMin != SamplerMinFilter::NEAREST; + } + private: - friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept { - return SamplerParams::LessThan{}(lhs, rhs); + friend bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept { + return EqualTo{}(lhs, rhs); + } + friend bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept { + return !EqualTo{}(lhs, rhs); + } + friend bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept { + return LessThan{}(lhs, rhs); } }; + static_assert(sizeof(SamplerParams) == 4); // The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code @@ -907,6 +1383,11 @@ static_assert(sizeof(SamplerParams) == 4); static_assert(sizeof(SamplerParams) <= sizeof(uint64_t), "SamplerParams must be no more than 64 bits"); +struct DescriptorSetLayout { + std::variant label; + utils::FixedCapacityVector descriptors; +}; + //! blending equation function enum class BlendEquation : uint8_t { ADD, //!< the fragment is added to the color buffer @@ -1052,7 +1533,7 @@ struct RasterState { bool inverseFrontFaces : 1; // 31 //! padding, must be 0 - uint8_t padding : 1; // 32 + bool depthClamp : 1; // 32 }; uint32_t u = 0; }; @@ -1063,32 +1544,6 @@ struct RasterState { * \privatesection */ -enum class ShaderStage : uint8_t { - VERTEX = 0, - FRAGMENT = 1, - COMPUTE = 2 -}; - -static constexpr size_t PIPELINE_STAGE_COUNT = 2; -enum class ShaderStageFlags : uint8_t { - NONE = 0, - VERTEX = 0x1, - FRAGMENT = 0x2, - COMPUTE = 0x4, - ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE -}; - -static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { - switch (type) { - case ShaderStage::VERTEX: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); - case ShaderStage::FRAGMENT: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT)); - case ShaderStage::COMPUTE: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE)); - } -} - /** * Selects which buffers to clear at the beginning of the render pass, as well as which buffers * can be discarded at the beginning and end of the render pass. @@ -1218,13 +1673,15 @@ struct StencilState { uint8_t padding = 0; }; +using PushConstantVariant = std::variant; + static_assert(sizeof(StencilState::StencilOperations) == 5u, "StencilOperations size not what was intended"); static_assert(sizeof(StencilState) == 12u, "StencilState size not what was intended"); -using FrameScheduledCallback = void(*)(PresentCallable callable, void* user); +using FrameScheduledCallback = utils::Invocable; enum class Workaround : uint16_t { // The EASU pass must split because shader compiler flattens early-exit branch @@ -1235,21 +1692,34 @@ enum class Workaround : uint16_t { // for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu ADRENO_UNIFORM_ARRAY_CRASH, // Workaround a Metal pipeline compilation error with the message: - // "Could not statically determine the target of a texture". See light_indirect.fs - A8X_STATIC_TEXTURE_TARGET_ERROR, + // "Could not statically determine the target of a texture". See surface_light_indirect.fs + METAL_STATIC_TEXTURE_TARGET_ERROR, // Adreno drivers sometimes aren't able to blit into a layer of a texture array. DISABLE_BLIT_INTO_TEXTURE_ARRAY, // Multiple workarounds needed for PowerVR GPUs POWER_VR_SHADER_WORKAROUNDS, + // Some browsers, such as Firefox on Mac, struggle with slow shader compile/link times when + // creating programs for the default material, leading to startup stutters. This workaround + // prevents these stutters by not precaching depth variants of the default material for those + // particular browsers. + DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL, + // Emulate an sRGB swapchain in shader code. + EMULATE_SRGB_SWAPCHAIN, }; -//! The type of technique for stereoscopic rendering -enum class StereoscopicType : uint8_t { - // Stereoscopic rendering is performed using instanced rendering technique. - INSTANCED, - // Stereoscopic rendering is performed using the multiview feature from the graphics backend. - MULTIVIEW, -}; +using StereoscopicType = Platform::StereoscopicType; + +using FrameTimestamps = Platform::FrameTimestamps; + +using CompositorTiming = Platform::CompositorTiming; + +using AsynchronousMode = Platform::AsynchronousMode; + +using AsyncCallId = uint32_t; + +static constexpr AsyncCallId InvalidAsyncCallId = std::numeric_limits::max(); + +using AsynchronousMode = Platform::AsynchronousMode; } // namespace filament::backend @@ -1257,10 +1727,17 @@ template<> struct utils::EnableBitMaskOperators struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; template<> struct utils::EnableBitMaskOperators : public std::true_type {}; template<> struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; + template<> struct utils::EnableIntegerOperators : public std::true_type {}; template<> struct utils::EnableIntegerOperators @@ -1289,12 +1766,16 @@ utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::Textu utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::TextureUsage usage); utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::BufferObjectBinding binding); utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::TextureSwizzle swizzle); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStage shaderStage); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStageFlags stageFlags); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::CompilerPriorityQueue compilerPriorityQueue); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::PushConstantVariant pushConstantVariant); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::AttributeArray& type); +utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::DescriptorSetLayout& dsl); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::PolygonOffset& po); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RasterState& rs); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RenderPassParams& b); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::Viewport& v); -utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStageFlags stageFlags); #endif #endif // TNT_FILAMENT_BACKEND_DRIVERENUMS_H diff --git a/package/android/libs/filament/include/backend/Handle.h b/package/android/libs/filament/include/backend/Handle.h index 4b63607a..c9b123c0 100644 --- a/package/android/libs/filament/include/backend/Handle.h +++ b/package/android/libs/filament/include/backend/Handle.h @@ -17,15 +17,17 @@ #ifndef TNT_FILAMENT_BACKEND_HANDLE_H #define TNT_FILAMENT_BACKEND_HANDLE_H -#if !defined(NDEBUG) -#include -#endif #include #include // FIXME: STL headers are not allowed in public headers +#include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { struct HwBufferObject; @@ -34,13 +36,16 @@ struct HwIndexBuffer; struct HwProgram; struct HwRenderPrimitive; struct HwRenderTarget; -struct HwSamplerGroup; struct HwStream; struct HwSwapChain; +struct HwSync; struct HwTexture; struct HwTimerQuery; struct HwVertexBufferInfo; struct HwVertexBuffer; +struct HwDescriptorSetLayout; +struct HwDescriptorSet; +struct HwMemoryMappedBuffer; /* * A handle to a backend resource. HandleBase is for internal use only. @@ -75,6 +80,19 @@ class HandleBase { HandleBase(HandleBase const& rhs) noexcept = default; HandleBase& operator=(HandleBase const& rhs) noexcept = default; + HandleBase(HandleBase&& rhs) noexcept + : object(rhs.object) { + rhs.object = nullid; + } + + HandleBase& operator=(HandleBase&& rhs) noexcept { + if (this != &rhs) { + object = rhs.object; + rhs.object = nullid; + } + return *this; + } + private: HandleId object; }; @@ -89,8 +107,20 @@ struct Handle : public HandleBase { Handle() noexcept = default; Handle(Handle const& rhs) noexcept = default; - - Handle& operator=(Handle const& rhs) noexcept = default; + Handle(Handle&& rhs) noexcept = default; + + // Explicitly redefine copy/move assignment operators rather than just using default here. + // Because it doesn't make a call to the parent's method automatically during the std::move + // function call(https://en.cppreference.com/w/cpp/algorithm/move) in certain compilers like + // NDK 25.1.8937393 and below (see b/371980551) + Handle& operator=(Handle const& rhs) noexcept { + HandleBase::operator=(rhs); + return *this; + } + Handle& operator=(Handle&& rhs) noexcept { + HandleBase::operator=(std::move(rhs)); + return *this; + } explicit Handle(HandleId id) noexcept : HandleBase(id) { } @@ -103,7 +133,7 @@ struct Handle : public HandleBase { bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); } // type-safe Handle cast - template::value> > + template> > Handle(Handle const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor) private: @@ -115,19 +145,22 @@ struct Handle : public HandleBase { // Types used by the command stream // (we use this renaming because the macro-system doesn't deal well with "<" and ">") -using BufferObjectHandle = Handle; -using FenceHandle = Handle; -using IndexBufferHandle = Handle; -using ProgramHandle = Handle; -using RenderPrimitiveHandle = Handle; -using RenderTargetHandle = Handle; -using SamplerGroupHandle = Handle; -using StreamHandle = Handle; -using SwapChainHandle = Handle; -using TextureHandle = Handle; -using TimerQueryHandle = Handle; -using VertexBufferHandle = Handle; -using VertexBufferInfoHandle = Handle; +using BufferObjectHandle = Handle; +using FenceHandle = Handle; +using IndexBufferHandle = Handle; +using ProgramHandle = Handle; +using RenderPrimitiveHandle = Handle; +using RenderTargetHandle = Handle; +using StreamHandle = Handle; +using SwapChainHandle = Handle; +using SyncHandle = Handle; +using TextureHandle = Handle; +using TimerQueryHandle = Handle; +using VertexBufferHandle = Handle; +using VertexBufferInfoHandle = Handle; +using DescriptorSetLayoutHandle = Handle; +using DescriptorSetHandle = Handle; +using MemoryMappedBufferHandle = Handle; } // namespace filament::backend diff --git a/package/android/libs/filament/include/backend/PipelineState.h b/package/android/libs/filament/include/backend/PipelineState.h index 220d04bb..106e579e 100644 --- a/package/android/libs/filament/include/backend/PipelineState.h +++ b/package/android/libs/filament/include/backend/PipelineState.h @@ -20,17 +20,27 @@ #include #include -#include +#include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { //! \privatesection +struct PipelineLayout { + using SetLayout = std::array, MAX_DESCRIPTOR_SET_COUNT>; + SetLayout setLayout; // 16 +}; + struct PipelineState { Handle program; // 4 Handle vertexBufferInfo; // 4 + PipelineLayout pipelineLayout; // 16 RasterState rasterState; // 4 StencilState stencilState; // 12 PolygonOffset polygonOffset; // 8 diff --git a/package/android/libs/filament/include/backend/PixelBufferDescriptor.h b/package/android/libs/filament/include/backend/PixelBufferDescriptor.h index c45f344d..93f82c9d 100644 --- a/package/android/libs/filament/include/backend/PixelBufferDescriptor.h +++ b/package/android/libs/filament/include/backend/PixelBufferDescriptor.h @@ -24,11 +24,14 @@ #include #include -#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { /** @@ -201,23 +204,15 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { }, new T(std::forward(functor)) }; } - - // -------------------------------------------------------------------------------------------- - /** - * Computes the size in bytes needed to fit an image of given dimensions and format + * Computes the size in bytes for a pixel of given dimensions and format * * @param format Format of the image pixels * @param type Type of the image pixels - * @param stride Stride of a row in pixels - * @param height Height of the image in rows - * @param alignment Alignment in bytes of pixel rows - * @return The buffer size needed to fit this image in bytes + * @return The size of the specified pixel in bytes */ - static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type, - size_t stride, size_t height, size_t alignment) noexcept { - assert_invariant(alignment); + static constexpr size_t computePixelSize(PixelDataFormat format, PixelDataType type) noexcept { if (type == PixelDataType::COMPRESSED) { return 0; } @@ -239,7 +234,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { case PixelDataFormat::RGB_INTEGER: n = 3; break; - case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm) + case PixelDataFormat::UNUSED:// shouldn't happen (used to be rgbm) case PixelDataFormat::RGBA: case PixelDataFormat::RGBA_INTEGER: n = 4; @@ -248,7 +243,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { size_t bpp = n; switch (type) { - case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings + case PixelDataType::COMPRESSED:// Impossible -- to squash the IDE warnings case PixelDataType::UBYTE: case PixelDataType::BYTE: // nothing to do @@ -279,16 +274,35 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { bpp = 2; break; } + return bpp; + } + + // -------------------------------------------------------------------------------------------- + + /** + * Computes the size in bytes needed to fit an image of given dimensions and format + * + * @param format Format of the image pixels + * @param type Type of the image pixels + * @param stride Stride of a row in pixels + * @param height Height of the image in rows + * @param alignment Alignment in bytes of pixel rows + * @return The buffer size needed to fit this image in bytes + */ + static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type, + size_t stride, size_t height, size_t alignment) noexcept { + assert_invariant(alignment); + size_t bpp = computePixelSize(format, type); size_t const bpr = bpp * stride; size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1); return bprAligned * height; } //! left coordinate in pixels - uint32_t left = 0; + uint32_t left = 0; //! top coordinate in pixels - uint32_t top = 0; + uint32_t top = 0; union { struct { //! stride in pixels diff --git a/package/android/libs/filament/include/backend/Platform.h b/package/android/libs/filament/include/backend/Platform.h index 03026dff..dcbc9928 100644 --- a/package/android/libs/filament/include/backend/Platform.h +++ b/package/android/libs/filament/include/backend/Platform.h @@ -19,14 +19,21 @@ #ifndef TNT_FILAMENT_BACKEND_PLATFORM_H #define TNT_FILAMENT_BACKEND_PLATFORM_H +#include #include #include +#include #include #include +#include +#include +#include + namespace filament::backend { +class CallbackHandler; class Driver; /** @@ -40,6 +47,235 @@ class UTILS_PUBLIC Platform { struct SwapChain {}; struct Fence {}; struct Stream {}; + struct Sync {}; + + using SyncCallback = void(*)(Sync* UTILS_NONNULL sync, void* UTILS_NULLABLE userData); + + class ExternalImageHandle; + + class ExternalImage { + friend class ExternalImageHandle; + std::atomic_uint32_t mRefCount{0}; + protected: + virtual ~ExternalImage() noexcept; + }; + + class ExternalImageHandle { + ExternalImage* UTILS_NULLABLE mTarget = nullptr; + static void incref(ExternalImage* UTILS_NULLABLE p) noexcept; + static void decref(ExternalImage* UTILS_NULLABLE p) noexcept; + + public: + ExternalImageHandle() noexcept; + ~ExternalImageHandle() noexcept; + explicit ExternalImageHandle(ExternalImage* UTILS_NULLABLE p) noexcept; + ExternalImageHandle(ExternalImageHandle const& rhs) noexcept; + ExternalImageHandle(ExternalImageHandle&& rhs) noexcept; + ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept; + ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept; + + bool operator==(const ExternalImageHandle& rhs) const noexcept { + return mTarget == rhs.mTarget; + } + explicit operator bool() const noexcept { return mTarget != nullptr; } + + ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; } + ExternalImage const* UTILS_NULLABLE get() const noexcept { return mTarget; } + + ExternalImage* UTILS_NULLABLE operator->() noexcept { return mTarget; } + ExternalImage const* UTILS_NULLABLE operator->() const noexcept { return mTarget; } + + ExternalImage& operator*() noexcept { return *mTarget; } + ExternalImage const& operator*() const noexcept { return *mTarget; } + + void clear() noexcept; + void reset(ExternalImage* UTILS_NULLABLE p) noexcept; + + private: + friend utils::io::ostream& operator<<(utils::io::ostream& out, + ExternalImageHandle const& handle); + }; + + using ExternalImageHandleRef = ExternalImageHandle const&; + + struct CompositorTiming { + /** duration in nanosecond since epoch of std::steady_clock */ + using time_point_ns = int64_t; + /** duration in nanosecond on the std::steady_clock */ + using duration_ns = int64_t; + static constexpr time_point_ns INVALID = -1; //!< value not supported + /** + * The timestamp [ns] since epoch of the next time the compositor will begin composition. + * This is effectively the deadline for when the compositor must receive a newly queued + * frame. + */ + time_point_ns compositeDeadline; + + /** + * The time delta [ns] between subsequent composition events. + */ + duration_ns compositeInterval; + + /** + * The time delta [ns] between the start of composition and the expected present time of + * that composition. This can be used to estimate the latency of the actual present time. + */ + duration_ns compositeToPresentLatency; + + /** + * The timestamp [ns] since epoch of the system's expected presentation time. + * INVALID if not supported. + */ + time_point_ns expectedPresentTime; + + /** + * The timestamp [ns] since epoch of the current frame's start (i.e. vsync) + * INVALID if not supported. + */ + time_point_ns frameTime; + + /** + * The timestamp [ns] since epoch of the current frame's deadline + * INVALID if not supported. + */ + time_point_ns frameTimelineDeadline; + }; + + struct FrameTimestamps { + /** duration in nanosecond since epoch of std::steady_clock */ + using time_point_ns = int64_t; + static constexpr time_point_ns INVALID = -1; //!< value not supported + static constexpr time_point_ns PENDING = -2; //!< value not yet available + + /** + * The time the application requested this frame be presented. + * If the application does not request a presentation time explicitly, + * this will correspond to buffer's queue time. + */ + time_point_ns requestedPresentTime; + + /** + * The time when all the application's rendering to the surface was completed. + */ + time_point_ns acquireTime; + + /** + * The time when the compositor selected this frame as the one to use for the next + * composition. This is the earliest indication that the frame was submitted in time. + */ + time_point_ns latchTime; + + /** + * The first time at which the compositor began preparing composition for this frame. + * Zero if composition was handled by the display and the compositor didn't do any + * rendering. + */ + time_point_ns firstCompositionStartTime; + + /** + * The last time at which the compositor began preparing composition for this frame, for + * frames composited more than once. Zero if composition was handled by the display and the + * compositor didn't do any rendering. + */ + time_point_ns lastCompositionStartTime; + + /** + * The time at which the compositor's rendering work for this frame finished. This will be + * INVALID if composition was handled by the display and the compositor didn't do any + * rendering. + */ + time_point_ns gpuCompositionDoneTime; + + /** + * The time at which this frame started to scan out to the physical display. + */ + time_point_ns displayPresentTime; + + /** + * The time when the buffer became available for reuse as a buffer the client can target + * without blocking. This is generally the point when all read commands of the buffer have + * been submitted, but not necessarily completed. + */ + time_point_ns dequeueReadyTime; + + /** + * The time at which all reads for the purpose of display/composition were completed for + * this frame. + */ + time_point_ns releaseTime; + }; + + /** + * The type of technique for stereoscopic rendering. (Note that the materials used will need to + * be compatible with the chosen technique.) + */ + enum class StereoscopicType : uint8_t { + /** + * No stereoscopic rendering + */ + NONE, + /** + * Stereoscopic rendering is performed using instanced rendering technique. + */ + INSTANCED, + /** + * Stereoscopic rendering is performed using the multiview feature from the graphics + * backend. + */ + MULTIVIEW, + }; + + /** + * This controls the priority level for GPU work scheduling, which helps prioritize the + * submitted GPU work and enables preemption. + */ + enum class GpuContextPriority : uint8_t { + /** + * Backend default GPU context priority (typically MEDIUM) + */ + DEFAULT, + /** + * For non-interactive, deferrable workloads. This should not interfere with standard + * applications. + */ + LOW, + /** + * The default priority level for standard applications. + */ + MEDIUM, + /** + * For high-priority, latency-sensitive workloads that are more important than standard + * applications. + */ + HIGH, + /** + * The highest priority, intended for system-critical, real-time applications where missing + * deadlines is unacceptable (e.g., VR/AR compositors or other system-critical tasks). + */ + REALTIME, + }; + + /** + * Defines how asynchronous operations are handled by the engine. + */ + enum class AsynchronousMode : uint8_t { + /** + * Asynchronous operations are disabled. This is the default. + */ + NONE, + + /** + * Attempts to use a dedicated worker thread for asynchronous tasks. If threading is not + * supported by the platform, it automatically falls back to using an amortization strategy. + */ + THREAD_PREFERRED, + + /** + * Uses an amortization strategy, processing a small number of asynchronous tasks during + * each engine update cycle. + */ + AMORTIZATION, + }; struct DriverConfig { /** @@ -48,23 +284,85 @@ class UTILS_PUBLIC Platform { */ size_t handleArenaSize = 0; - /** - * This number of most-recently destroyed textures will be tracked for use-after-free. - * Throws an exception when a texture is freed but still bound to a SamplerGroup and used in - * a draw call. 0 disables completely. Currently only respected by the Metal backend. - */ - size_t textureUseAfterFreePoolSize = 0; + size_t metalUploadBufferSizeBytes = 512 * 1024; /** * Set to `true` to forcibly disable parallel shader compilation in the backend. - * Currently only honored by the GL and Metal backends. + * Currently only honored by the GL and Metal backends, and the Vulkan backend + * when some experimental features are enabled. */ bool disableParallelShaderCompile = false; + /** + * Set to `true` to forcibly disable amortized shader compilation in the backend. + * Currently only honored by the GL backend. + */ + bool disableAmortizedShaderCompile = true; + /** * Disable backend handles use-after-free checks. */ bool disableHandleUseAfterFreeCheck = false; + + /** + * Disable backend handles tags for heap allocated (fallback) handles + */ + bool disableHeapHandleTags = false; + + /** + * Force GLES2 context if supported, or pretend the context is ES2. Only meaningful on + * GLES 3.x backends. + */ + bool forceGLES2Context = false; + + /** + * Sets the technique for stereoscopic rendering. + */ + StereoscopicType stereoscopicType = StereoscopicType::NONE; + + /** + * Assert the native window associated to a SwapChain is valid when calling makeCurrent(). + * This is only supported for: + * - PlatformEGLAndroid + */ + bool assertNativeWindowIsValid = false; + + /** + * The action to take if a Drawable cannot be acquired. If true, the + * frame is aborted instead of panic. This is only supported for: + * - PlatformMetal + */ + bool metalDisablePanicOnDrawableFailure = false; + + /** + * GPU context priority level. Controls GPU work scheduling and preemption. + * This is only supported for: + * - PlatformEGL + */ + GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT; + + /** + * Enables asynchronous pipeline cache preloading, if supported on this device. + * This is only supported for: + * - VulkanPlatform + * When the following device extensions are available: + * - VK_KHR_dynamic_rendering + * - VK_EXT_vertex_input_dynamic_state + * Should be enabled only for devices where it has been shown this is effective. + */ + bool vulkanEnableAsyncPipelineCachePrewarming = false; + + /** + * Bypass the staging buffer because the device is of Unified Memory Architecture. + * This is only supported for: + * - VulkanPlatform + */ + bool vulkanEnableStagingBufferBypass = false; + + /** + * Asynchronous mode for the engine. Defines how asynchronous operations are handled. + */ + AsynchronousMode asynchronousMode = AsynchronousMode::NONE; }; Platform() noexcept; @@ -85,13 +383,13 @@ class UTILS_PUBLIC Platform { * @param sharedContext an optional shared context. This is not meaningful with all graphic * APIs and platforms. * For EGL platforms, this is an EGLContext. - * + * * @param driverConfig specifies driver initialization parameters * * @return nullptr on failure, or a pointer to the newly created driver. */ - virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext, - const DriverConfig& driverConfig) noexcept = 0; + virtual Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext, + const DriverConfig& driverConfig) = 0; /** * Processes the platform's event queue when called from its primary event-handling thread. @@ -102,6 +400,65 @@ class UTILS_PUBLIC Platform { */ virtual bool pumpEvents() noexcept; + // -------------------------------------------------------------------------------------------- + // Swapchain timing APIs + + /** + * Whether this platform supports compositor timing querying. + * + * @return true if this Platform supports compositor timings, false otherwise [default] + * @see queryCompositorTiming() + * @see setPresentFrameId() + * @see queryFrameTimestamps() + */ + virtual bool isCompositorTimingSupported() const noexcept; + + /** + * If compositor timing is supported, fills the provided CompositorTiming structure + * with timing information form the compositor the swapchain's native window is using. + * The swapchain'snative window must be valid (i.e. not a headless swapchain). + * @param swapchain to query the compositor timing from + * @return true on success, false otherwise (e.g. if not supported) + * @see isCompositorTimingSupported() + */ + virtual bool queryCompositorTiming(SwapChain const* UTILS_NONNULL swapchain, + CompositorTiming* UTILS_NONNULL outCompositorTiming) const noexcept; + + /** + * Associate a generic frameId which must be monotonically increasing (albeit not strictly) with + * the next frame to be presented on the specified swapchain. + * + * This must be called from the backend thread. + * + * @param swapchain + * @param frameId + * @return true on success, false otherwise + * @see isCompositorTimingSupported() + * @see queryFrameTimestamps() + */ + virtual bool setPresentFrameId(SwapChain const* UTILS_NONNULL swapchain, + uint64_t frameId) noexcept; + + /** + * If compositor timing is supported, fills the provided FrameTimestamps structure + * with timing information of a given frame, identified by the frame id, of the specified + * swapchain. The system only keeps a limited history of frames timings. + * + * This API is thread safe and can be called from any thread. + * + * @param swapchain swapchain to query the timestamps of + * @param frameId frame we're interested it + * @param outFrameTimestamps output structure receiving the timestamps + * @return true if successful, false otherwise + * @see isCompositorTimingSupported() + * @see setPresentFrameId() + */ + virtual bool queryFrameTimestamps(SwapChain const* UTILS_NONNULL swapchain, + uint64_t frameId, FrameTimestamps* UTILS_NONNULL outFrameTimestamps) const noexcept; + + // -------------------------------------------------------------------------------------------- + // Caching APIs + /** * InsertBlobFunc is an Invocable to an application-provided function that a * backend implementation may use to insert a key/value pair into the @@ -194,13 +551,25 @@ class UTILS_PUBLIC Platform { size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize, void* UTILS_NONNULL value, size_t valueSize); - using DebugUpdateStatFunc = utils::Invocable; + // -------------------------------------------------------------------------------------------- + // Debugging APIs + + using DebugUpdateStatFunc = utils::Invocable; /** * Sets the callback function that the backend can use to update backend-specific statistics * to aid with debugging. This callback is guaranteed to be called on the Filament driver * thread. * + * The callback signature is (key, intValue, stringValue). Note that for any given call, + * only one of the value parameters (intValue or stringValue) will be meaningful, depending on + * the specific key. + * + * IMPORTANT_NOTE: because the callback is called on the driver thread, only quick, non-blocking + * work should be done inside it. Furthermore, no graphics API calls (such as GL calls) should + * be made, which could interfere with Filament's driver state. + * * @param debugUpdateStat an Invocable that updates debug statistics */ void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept; @@ -219,15 +588,32 @@ class UTILS_PUBLIC Platform { * This function is guaranteed to be called only on a single thread, the Filament driver * thread. * - * @param key a null-terminated C-string with the key of the debug statistic - * @param value the updated value of key + * @param key a null-terminated C-string with the key of the debug statistic + * @param intValue the updated integer value of key (the string value passed to the + * callback will be empty) + */ + void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue); + + /** + * To track backend-specific statistics, the backend implementation can call the + * application-provided callback function debugUpdateStatFunc to associate or update a value + * with a given key. It is possible for this function to be called multiple times with the + * same key, in which case newer values should overwrite older values. + * + * This function is guaranteed to be called only on a single thread, the Filament driver + * thread. + * + * @param key a null-terminated C-string with the key of the debug statistic + * @param stringValue the updated string value of key (the integer value passed to the + * callback will be 0) */ - void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value); + void debugUpdateStat(const char* UTILS_NONNULL key, utils::CString stringValue); private: - InsertBlobFunc mInsertBlob; - RetrieveBlobFunc mRetrieveBlob; - DebugUpdateStatFunc mDebugUpdateStat; + std::shared_ptr mInsertBlob; + std::shared_ptr mRetrieveBlob; + std::shared_ptr mDebugUpdateStat; + mutable utils::Mutex mMutex; }; } // namespace filament diff --git a/package/android/libs/filament/include/backend/PresentCallable.h b/package/android/libs/filament/include/backend/PresentCallable.h index 4402f222..ea3380c6 100644 --- a/package/android/libs/filament/include/backend/PresentCallable.h +++ b/package/android/libs/filament/include/backend/PresentCallable.h @@ -27,12 +27,12 @@ namespace filament::backend { * A PresentCallable is a callable object that, when called, schedules a frame for presentation on * a SwapChain. * - * Typically, Filament's backend is responsible scheduling a frame's presentation. However, there - * are certain cases where the application might want to control when a frame is scheduled for + * Typically, Filament's backend is responsible for scheduling a frame's presentation. However, + * there are certain cases where the application might want to control when a frame is scheduled for * presentation. * * For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present - * within a CATransation: + * within a CATransaction: * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * void myFrameScheduledCallback(PresentCallable presentCallable, void* user) { @@ -48,21 +48,20 @@ namespace filament::backend { * and optional user data: * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * swapChain->setFrameScheduledCallback(myFrameScheduledCallback, nullptr); + * swapChain->setFrameScheduledCallback(nullptr, myFrameScheduledCallback); * if (renderer->beginFrame(swapChain)) { * renderer->render(view); * renderer->endFrame(); * } * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other - * backends ignore the callback (which will never be called) and proceed normally. + * @remark The PresentCallable mechanism for user-controlled presentation is only supported by + * Filament's Metal backend. On other backends, the FrameScheduledCallback is still invoked, but the + * PresentCallable passed to it is a no-op and calling it has no effect. * - * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. - * - * Applications *must* call each PresentCallable they receive. Each PresentCallable represents a - * frame that is waiting to be presented. If an application fails to call a PresentCallable, a - * memory leak could occur. To "cancel" the presentation of a frame, pass false to the + * When using the Metal backend, applications *must* call each PresentCallable they receive. Each + * PresentCallable represents a frame that is waiting to be presented, and failing to call it + * will result in a memory leak. To "cancel" the presentation of a frame, pass false to the * PresentCallable, which will cancel the presentation of the frame and release associated memory. * * @see Renderer, SwapChain::setFrameScheduledCallback @@ -71,6 +70,7 @@ class UTILS_PUBLIC PresentCallable { public: using PresentFn = void(*)(bool presentFrame, void* user); + static void noopPresent(bool, void*) {} PresentCallable(PresentFn fn, void* user) noexcept; ~PresentCallable() noexcept = default; diff --git a/package/android/libs/filament/include/backend/Program.h b/package/android/libs/filament/include/backend/Program.h index fe1c4a9b..431e939e 100644 --- a/package/android/libs/filament/include/backend/Program.h +++ b/package/android/libs/filament/include/backend/Program.h @@ -20,17 +20,21 @@ #include #include #include -#include #include -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers +#include +#include +#include +#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { class Program { @@ -40,36 +44,40 @@ class Program { static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT; static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT; - struct Sampler { - utils::CString name = {}; // name of the sampler in the shader - uint32_t binding = 0; // binding point of the sampler in the shader + struct Descriptor { + utils::CString name; + DescriptorType type; + descriptor_binding_t binding; }; - struct SamplerGroupData { - utils::FixedCapacityVector samplers; - ShaderStageFlags stageFlags = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS; - }; + using SpecializationConstant = std::variant; + using DescriptorSetLayoutArray = std::array; - struct Uniform { + struct Uniform { // For ES2 support utils::CString name; // full qualified name of the uniform field uint16_t offset; // offset in 'uint32_t' into the uniform buffer uint8_t size; // >1 for arrays UniformType type; // uniform type }; - using UniformBlockInfo = std::array; - using UniformInfo = utils::FixedCapacityVector; - using SamplerGroupInfo = std::array; + using DescriptorBindingsInfo = utils::FixedCapacityVector; + using DescriptorSetInfo = std::array; + using SpecializationConstantsInfo = utils::FixedCapacityVector; using ShaderBlob = utils::FixedCapacityVector; using ShaderSource = std::array; + using AttributesInfo = utils::FixedCapacityVector>; + using UniformInfo = utils::FixedCapacityVector; + using BindingUniformsInfo = utils::FixedCapacityVector< + std::tuple>; + Program() noexcept; Program(const Program& rhs) = delete; Program& operator=(const Program& rhs) = delete; Program(Program&& rhs) noexcept; - Program& operator=(Program&& rhs) noexcept = delete; + Program& operator=(Program&& rhs) noexcept; ~Program() noexcept; @@ -77,91 +85,116 @@ class Program { // sets the material name and variant for diagnostic purposes only Program& diagnostics(utils::CString const& name, - utils::Invocable&& logger); + utils::Invocable&& logger); - // sets one of the program's shader (e.g. vertex, fragment) + // Sets one of the program's shader (e.g. vertex, fragment) // string-based shaders are null terminated, consequently the size parameter must include the // null terminating character. Program& shader(ShaderStage shader, void const* data, size_t size); - // Note: This is only needed for GLES3.0 backends, because the layout(binding=) syntax is - // not permitted in glsl. The backend needs a way to associate a uniform block - // to a binding point. - Program& uniformBlockBindings( - utils::FixedCapacityVector> const& uniformBlockBindings) noexcept; - - // Note: This is only needed for GLES2.0, this is used to emulate UBO. This function tells - // the program everything it needs to know about the uniforms at a given binding - Program& uniforms(uint32_t index, UniformInfo const& uniforms) noexcept; - - // Note: This is only needed for GLES2.0. - Program& attributes( - utils::FixedCapacityVector> attributes) noexcept; - - // sets the 'bindingPoint' sampler group descriptor for this program. - // 'samplers' can be destroyed after this call. - // This effectively associates a set of (BindingPoints, index) to a texture unit in the shader. - // Or more precisely, what layout(binding=) is set to in GLSL. - Program& setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags, - Sampler const* samplers, size_t count) noexcept; - - struct SpecializationConstant { - using Type = std::variant; - uint32_t id; // id set in glsl - Type value; // value and type + // Sets the language of the shader sources provided with shader() (defaults to ESSL3) + Program& shaderLanguage(ShaderLanguage shaderLanguage); + + // Descriptor binding (set, binding, type -> shader name) info + Program& descriptorBindings(backend::descriptor_set_t set, + DescriptorBindingsInfo descriptorBindings) noexcept; + + Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept; + + struct PushConstant { + utils::CString name; + ConstantType type; }; - Program& specializationConstants( - utils::FixedCapacityVector specConstants) noexcept; + Program& pushConstants(ShaderStage stage, + utils::FixedCapacityVector constants) noexcept; Program& cacheId(uint64_t cacheId) noexcept; Program& multiview(bool multiview) noexcept; + // For ES2 support only... + Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms); + Program& attributes(AttributesInfo attributes) noexcept; + + // + // Getters for program construction... + // + ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } ShaderSource& getShadersSource() noexcept { return mShadersSource; } - UniformBlockInfo const& getUniformBlockBindings() const noexcept { return mUniformBlocks; } - UniformBlockInfo& getUniformBlockBindings() noexcept { return mUniformBlocks; } + utils::CString const& getName() const noexcept { return mName; } + utils::CString& getName() noexcept { return mName; } - SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; } - SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; } + auto const& getShaderLanguage() const { return mShaderLanguage; } - auto const& getBindingUniformInfo() const { return mBindingUniformInfo; } - auto& getBindingUniformInfo() { return mBindingUniformInfo; } + uint64_t getCacheId() const noexcept { return mCacheId; } - auto const& getAttributes() const { return mAttributes; } - auto& getAttributes() { return mAttributes; } + bool isMultiview() const noexcept { return mMultiview; } - utils::CString const& getName() const noexcept { return mName; } - utils::CString& getName() noexcept { return mName; } + CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } - utils::FixedCapacityVector const& getSpecializationConstants() const noexcept { + SpecializationConstantsInfo const& getSpecializationConstants() const noexcept { return mSpecializationConstants; } - utils::FixedCapacityVector& getSpecializationConstants() noexcept { + + SpecializationConstantsInfo& getSpecializationConstants() noexcept { return mSpecializationConstants; } - uint64_t getCacheId() const noexcept { return mCacheId; } + DescriptorSetInfo& getDescriptorBindings() noexcept { + return mDescriptorBindings; + } - bool isMultiview() const noexcept { return mMultiview; } + inline Program& descriptorLayout(backend::descriptor_set_t set, + DescriptorSetLayout descriptorLayout) noexcept { + mDescriptorLayouts[set] = std::move(descriptorLayout); + return *this; + } - CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } + const DescriptorSetLayoutArray& getDescriptorSetLayouts() const noexcept { + return mDescriptorLayouts; + } + + utils::FixedCapacityVector const& getPushConstants( + ShaderStage stage) const noexcept { + return mPushConstants[static_cast(stage)]; + } + + utils::FixedCapacityVector& getPushConstants(ShaderStage stage) noexcept { + return mPushConstants[static_cast(stage)]; + } + + auto const& getBindingUniformInfo() const { return mBindingUniformsInfo; } + auto& getBindingUniformInfo() { return mBindingUniformsInfo; } + + auto const& getAttributes() const { return mAttributes; } + auto& getAttributes() { return mAttributes; } private: friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder); - UniformBlockInfo mUniformBlocks = {}; - SamplerGroupInfo mSamplerGroups = {}; ShaderSource mShadersSource; + ShaderLanguage mShaderLanguage = ShaderLanguage::ESSL3; utils::CString mName; uint64_t mCacheId{}; - utils::Invocable mLogger; - utils::FixedCapacityVector mSpecializationConstants; - utils::FixedCapacityVector> mAttributes; - std::array mBindingUniformInfo; CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; + utils::Invocable + mLogger; + SpecializationConstantsInfo mSpecializationConstants; + std::array, SHADER_TYPE_COUNT> mPushConstants; + DescriptorSetInfo mDescriptorBindings; + + // Descriptions for descriptor set layouts that may be used for this Program, which + // can be useful for attempting to compile the pipeline ahead of time. + DescriptorSetLayoutArray mDescriptorLayouts; + + // For ES2 support only + AttributesInfo mAttributes; + BindingUniformsInfo mBindingUniformsInfo; + // Indicates the current engine was initialized with multiview stereo, and the variant for this // program contains STE flag. This will be referred later for the OpenGL shader compiler to // determine whether shader code replacement for the num_views should be performed. diff --git a/package/android/libs/filament/include/backend/TargetBufferInfo.h b/package/android/libs/filament/include/backend/TargetBufferInfo.h index ce23fc5f..c2538161 100644 --- a/package/android/libs/filament/include/backend/TargetBufferInfo.h +++ b/package/android/libs/filament/include/backend/TargetBufferInfo.h @@ -19,27 +19,47 @@ #include -#include +#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { //! \privatesection struct TargetBufferInfo { + // note: the parameters of this constructor are not in the order of this structure's fields + TargetBufferInfo(Handle handle, uint8_t const level, uint16_t const layer) noexcept + : handle(std::move(handle)), level(level), layer(layer) { + } + + TargetBufferInfo(Handle handle, uint8_t const level) noexcept + : handle(handle), level(level) { + } + + TargetBufferInfo(Handle handle) noexcept // NOLINT(*-explicit-constructor) + : handle(handle) { + } + + TargetBufferInfo() noexcept = default; + // texture to be used as render target Handle handle; - // starting layer index for multiview. This value is only used when the `layerCount` for the - // render target is greater than 1. - uint8_t baseViewIndex = 0; - // level to be used uint8_t level = 0; - // for cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping + // - For cubemap textures, this indicates the face of the cubemap. See TextureCubemapFace for + // the face->layer mapping) + // - For 2d array, cubemap array, and 3d textures, this indicates an index of a single layer of + // them. + // - For multiview textures (i.e., layerCount for the RenderTarget is greater than 1), this + // indicates a starting layer index of the current 2d array texture for multiview. uint16_t layer = 0; }; @@ -54,17 +74,17 @@ class MRT { TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT]; public: - TargetBufferInfo const& operator[](size_t i) const noexcept { + TargetBufferInfo const& operator[](size_t const i) const noexcept { return mInfos[i]; } - TargetBufferInfo& operator[](size_t i) noexcept { + TargetBufferInfo& operator[](size_t const i) noexcept { return mInfos[i]; } MRT() noexcept = default; - MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions) + MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions, *-explicit-constructor) : mInfos{ color } { } @@ -84,7 +104,7 @@ class MRT { // this is here for backward compatibility MRT(Handle handle, uint8_t level, uint16_t layer) noexcept - : mInfos{{ handle, 0, level, layer }} { + : mInfos{{ handle, level, layer }} { } }; diff --git a/package/android/libs/filament/include/backend/platforms/AndroidNdk.h b/package/android/libs/filament/include/backend/platforms/AndroidNdk.h new file mode 100644 index 00000000..a3766710 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/AndroidNdk.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 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 FILAMENT_BACKEND_ANDROIDNDK_H +#define FILAMENT_BACKEND_ANDROIDNDK_H + +#include +#include + +#define FILAMENT_REQUIRES_API(x) __attribute__((__availability__(android,introduced=x))) + +#define FILAMENT_USE_DLSYM(api) (__ANDROID_API__ < (api)) + +namespace filament::backend { + +class AndroidNdk { +public: + AndroidNdk(); + +#if FILAMENT_USE_DLSYM(26) + + static void AHardwareBuffer_acquire( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_acquire(buffer); + } + + static void AHardwareBuffer_release( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_release(buffer); + } + + static void AHardwareBuffer_describe( + AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_describe(buffer, desc); + } + +#else + + static void AHardwareBuffer_acquire( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_acquire(buffer); + } + static void AHardwareBuffer_release( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_release(buffer); + } + static void AHardwareBuffer_describe( + AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_describe(buffer, desc); + } + +#endif + +private: +#if FILAMENT_USE_DLSYM(26) + static struct Ndk { + void (*AHardwareBuffer_acquire)(AHardwareBuffer*); + void (*AHardwareBuffer_release)(AHardwareBuffer*); + void (*AHardwareBuffer_describe)(AHardwareBuffer const*, AHardwareBuffer_Desc*); + } ndk; +#endif +}; + +} // filament::backend + +#endif //FILAMENT_BACKEND_ANDROIDNDK_H diff --git a/package/android/libs/filament/include/backend/platforms/OpenGLPlatform.h b/package/android/libs/filament/include/backend/platforms/OpenGLPlatform.h index dec6f47b..d71548e3 100644 --- a/package/android/libs/filament/include/backend/platforms/OpenGLPlatform.h +++ b/package/android/libs/filament/include/backend/platforms/OpenGLPlatform.h @@ -23,9 +23,11 @@ #include #include +#include #include #include +#include namespace filament::backend { @@ -51,12 +53,23 @@ class OpenGLPlatform : public Platform { ~OpenGLPlatform() noexcept override; public: - struct ExternalTexture { - unsigned int target; // GLenum target - unsigned int id; // GLuint id + unsigned int target; // GLenum target + unsigned int id; // GLuint id }; + /** + * Return the OpenGL vendor string of the specified Driver instance. + * @return The GL_VENDOR string + */ + static utils::CString getVendorString(Driver const* UTILS_NONNULL driver); + + /** + * Return the OpenGL vendor string of the specified Driver instance + * @return The GL_RENDERER string + */ + static utils::CString getRendererString(Driver const* UTILS_NONNULL driver); + /** * Called by the driver to destroy the OpenGL context. This should clean up any windows * or buffers from initialization. This is for instance where `eglDestroyContext` would be @@ -72,6 +85,15 @@ class OpenGLPlatform : public Platform { */ virtual bool isSRGBSwapChainSupported() const noexcept; + /** + * Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES flag. + * The default implementation returns false. + * + * @param samples The number of samples + * @return true if SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES is supported, false otherwise. + */ + virtual bool isMSAASwapChainSupported(uint32_t samples) const noexcept; + /** * Return whether protected contexts are supported by this backend. * If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be @@ -90,7 +112,7 @@ class OpenGLPlatform : public Platform { * */ virtual SwapChain* UTILS_NULLABLE createSwapChain( - void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0; + void* UTILS_NULLABLE nativeWindow, uint64_t flags) = 0; /** * Called by the driver create a headless SwapChain. @@ -104,7 +126,7 @@ class OpenGLPlatform : public Platform { * A void* might be enough. */ virtual SwapChain* UTILS_NULLABLE createSwapChain( - uint32_t width, uint32_t height, uint64_t flags) noexcept = 0; + uint32_t width, uint32_t height, uint64_t flags) = 0; /** * Called by the driver to destroys the SwapChain @@ -140,6 +162,23 @@ class OpenGLPlatform : public Platform { */ virtual uint32_t getDefaultFramebufferObject() noexcept; + /** + * Called by the backend when a frame starts. + * @param steady_clock_ns vsync time point on the monotonic clock + * @param refreshIntervalNs refresh interval in nanosecond + * @param frameId a frame id + */ + virtual void beginFrame( + int64_t monotonic_clock_ns, + int64_t refreshIntervalNs, + uint32_t frameId) noexcept; + + /** + * Called by the backend when a frame ends. + * @param frameId the frame id used in beginFrame + */ + virtual void endFrame( + uint32_t frameId) noexcept; /** * Type of contexts available @@ -169,7 +208,7 @@ class OpenGLPlatform : public Platform { */ virtual bool makeCurrent(ContextType type, SwapChain* UTILS_NONNULL drawSwapChain, - SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0; + SwapChain* UTILS_NONNULL readSwapChain) = 0; /** * Called by the driver to make the OpenGL context active on the calling thread and bind @@ -189,7 +228,13 @@ class OpenGLPlatform : public Platform { SwapChain* UTILS_NONNULL drawSwapChain, SwapChain* UTILS_NONNULL readSwapChain, utils::Invocable preContextChange, - utils::Invocable postContextChange) noexcept; + utils::Invocable postContextChange); + + /** + * Called by the backend just before calling commit() + * @see commit() + */ + virtual void preCommit() noexcept; /** * Called by the driver once the current frame finishes drawing. Typically, this should present @@ -240,6 +285,26 @@ class OpenGLPlatform : public Platform { */ virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept; + // -------------------------------------------------------------------------------------------- + // Sync support + + /** + * Creates a Sync. These can be used for frame synchronization externally + * (certain platform implementations can be exported to handles that can + * be used in other processes). + * + * @return A Sync object. + */ + virtual Platform::Sync* UTILS_NONNULL createSync() noexcept; + + /** + * Destroys a sync. If called with a sync not created by this platform + * object, this will lead to undefined behavior. + * + * @param sync The sync to destroy, that was created by this platform + * instance. + */ + virtual void destroySync(Platform::Sync* UTILS_NONNULL sync) noexcept; // -------------------------------------------------------------------------------------------- // Streaming support @@ -284,6 +349,13 @@ class OpenGLPlatform : public Platform { virtual void updateTexImage(Stream* UTILS_NONNULL stream, int64_t* UTILS_NONNULL timestamp) noexcept; + /** + * Returns the transform matrix of the texture attached to the stream. + * @param stream Stream to get the transform matrix from + * @param uvTransform Output parameter: Transform matrix of the image bound to the texture. Returns identity if not supported. + */ + virtual math::mat3f getTransformMatrix(Stream* UTILS_NONNULL stream) noexcept; + // -------------------------------------------------------------------------------------------- // External Image support @@ -301,36 +373,45 @@ class OpenGLPlatform : public Platform { * Destroys an external texture handle and associated data. * @param texture a pointer to the handle to destroy. */ - virtual void destroyExternalImage(ExternalTexture* UTILS_NONNULL texture) noexcept; + virtual void destroyExternalImageTexture(ExternalTexture* UTILS_NONNULL texture) noexcept; // called on the application thread to allow Filament to take ownership of the image /** * Takes ownership of the externalImage. The externalImage parameter depends on the Platform's - * concrete implementation. Ownership is released when destroyExternalImage() is called. + * concrete implementation. Ownership is released when destroyExternalImageTexture() is called. * * WARNING: This is called synchronously from the application thread (NOT the Driver thread) * * @param externalImage A token representing the platform's external image. * @see destroyExternalImage + * @{ */ virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept; + virtual void retainExternalImage(ExternalImageHandleRef externalImage) noexcept; + /** @}*/ + /** * Called to bind the platform-specific externalImage to an ExternalTexture. * ExternalTexture::id is guaranteed to be bound when this method is called and ExternalTexture * is updated with new values for id/target if necessary. * * WARNING: this method is not allowed to change the bound texture, or must restore the previous - * binding upon return. This is to avoid problem with a backend doing state caching. + * binding upon return. This is to avoid a problem with a backend doing state caching. * * @param externalImage The platform-specific external image. * @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary. * @return true on success, false on error. + * @{ */ virtual bool setExternalImage(void* UTILS_NONNULL externalImage, ExternalTexture* UTILS_NONNULL texture) noexcept; + virtual bool setExternalImage(ExternalImageHandleRef externalImage, + ExternalTexture* UTILS_NONNULL texture) noexcept; + /** @}*/ + /** * The method allows platforms to convert a user-supplied external image object into a new type * (e.g. HardwareBuffer => EGLImage). The default implementation returns source. @@ -349,7 +430,7 @@ class OpenGLPlatform : public Platform { virtual bool isExtraContextSupported() const noexcept; /** - * Creates an OpenGL context with the same configuration than the main context and makes it + * Creates an OpenGL context with the same configuration as the main context and makes it * current to the current thread. Must not be called from the main driver thread. * createContext() is only supported if isExtraContextSupported() returns true. * These additional contexts will be automatically terminated in terminate. @@ -362,7 +443,7 @@ class OpenGLPlatform : public Platform { /** * Detach and destroy the current context if any and releases all resources associated to - * this thread. + * this thread. This must be called from the same thread where createContext() was called. */ virtual void releaseContext() noexcept; }; diff --git a/package/android/libs/filament/include/backend/platforms/PlatformCocoaGL.h b/package/android/libs/filament/include/backend/platforms/PlatformCocoaGL.h index 97c9c3ce..c424ec4f 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformCocoaGL.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformCocoaGL.h @@ -17,7 +17,6 @@ #ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H #define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H -#include #include #include @@ -34,12 +33,14 @@ class PlatformCocoaGL : public OpenGLPlatform { PlatformCocoaGL(); ~PlatformCocoaGL() noexcept override; + ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept; + protected: // -------------------------------------------------------------------------------------------- // Platform Interface Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; // Currently returns 0 int getOSVersion() const noexcept override; @@ -57,12 +58,14 @@ class PlatformCocoaGL : public OpenGLPlatform { SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; - OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; - void destroyExternalImage(ExternalTexture* texture) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; void retainExternalImage(void* externalImage) noexcept override; bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override; + void retainExternalImage(ExternalImageHandleRef externalImage) noexcept override; + bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override; private: PlatformCocoaGLImpl* pImpl = nullptr; diff --git a/package/android/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h b/package/android/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h index e7f1d1ff..94ca86d1 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h @@ -32,11 +32,13 @@ class PlatformCocoaTouchGL : public OpenGLPlatform { PlatformCocoaTouchGL(); ~PlatformCocoaTouchGL() noexcept override; + ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept; + // -------------------------------------------------------------------------------------------- // Platform Interface Driver* createDriver(void* sharedGLContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; int getOSVersion() const noexcept final { return 0; } @@ -53,13 +55,15 @@ class PlatformCocoaTouchGL : public OpenGLPlatform { SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; - OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; - void destroyExternalImage(ExternalTexture* texture) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; void retainExternalImage(void* externalImage) noexcept override; bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override; + void retainExternalImage(ExternalImageHandleRef externalImage) noexcept override; + bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override; private: PlatformCocoaTouchGLImpl* pImpl = nullptr; diff --git a/package/android/libs/filament/include/backend/platforms/PlatformEGL.h b/package/android/libs/filament/include/backend/platforms/PlatformEGL.h index ef687653..8b8f2a95 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformEGL.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformEGL.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -47,6 +48,11 @@ class PlatformEGL : public OpenGLPlatform { // Return true if we're on an OpenGL platform (as opposed to OpenGL ES). false by default. virtual bool isOpenGL() const noexcept; + /** + * Creates an ExternalImage from a EGLImageKHR + */ + ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept; + protected: // -------------------------------------------------------------------------------------------- // Helper for EGL configs and attributes parameters @@ -75,8 +81,7 @@ class PlatformEGL : public OpenGLPlatform { * Initializes EGL, creates the OpenGL context and returns a concrete Driver implementation * that supports OpenGL/OpenGL ES. */ - Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) override; /** * This returns zero. This method can be overridden to return something more useful. @@ -94,22 +99,19 @@ class PlatformEGL : public OpenGLPlatform { void terminate() noexcept override; bool isProtectedContextSupported() const noexcept override; - bool isSRGBSwapChainSupported() const noexcept override; - SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; - SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; - void destroySwapChain(SwapChain* swapChain) noexcept override; + bool isMSAASwapChainSupported(uint32_t samples) const noexcept override; bool isSwapChainProtected(SwapChain* swapChain) noexcept override; ContextType getCurrentContextType() const noexcept override; bool makeCurrent(ContextType type, SwapChain* drawSwapChain, - SwapChain* readSwapChain) noexcept override; + SwapChain* readSwapChain) override; void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain, utils::Invocable preContextChange, - utils::Invocable postContextChange) noexcept override; + utils::Invocable postContextChange) override; void commit(SwapChain* swapChain) noexcept override; @@ -118,12 +120,13 @@ class PlatformEGL : public OpenGLPlatform { void destroyFence(Fence* fence) noexcept override; FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept override; - OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; - void destroyExternalImage(ExternalTexture* texture) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override; + bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override; /** - * Logs glGetError() to slog.e + * Logs glGetError() to LOG(ERROR) * @param name a string giving some context on the error. Typically __func__. */ static void logEglError(const char* name) noexcept; @@ -143,25 +146,18 @@ class PlatformEGL : public OpenGLPlatform { EGLContext getContextForType(ContextType type) const noexcept; // makes the draw and read surface current without changing the current context - EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept { + EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) { return egl.makeCurrent(drawSurface, readSurface); } // makes context current and set draw and read surfaces to EGL_NO_SURFACE - EGLBoolean makeCurrent(EGLContext context) noexcept { + EGLBoolean makeCurrent(EGLContext context) { return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface); } - // TODO: this should probably use getters instead. - EGLDisplay mEGLDisplay = EGL_NO_DISPLAY; - EGLContext mEGLContext = EGL_NO_CONTEXT; - EGLContext mEGLContextProtected = EGL_NO_CONTEXT; - EGLSurface mEGLDummySurface = EGL_NO_SURFACE; - ContextType mCurrentContextType = ContextType::NONE; - // mEGLConfig is valid only if ext.egl.KHR_no_config_context is false - EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR; - Config mContextAttribs; - std::vector mAdditionalContexts; + EGLDisplay getEglDisplay() const noexcept { return mEGLDisplay; } + EGLConfig getEglConfig() const noexcept { return mEGLConfig; } + EGLConfig getSuitableConfigForSwapChain(uint64_t flags, bool window, bool pbuffer) const; // supported extensions detected at runtime struct { @@ -178,7 +174,11 @@ class PlatformEGL : public OpenGLPlatform { } egl; } ext; - struct SwapChainEGL : public Platform::SwapChain { + struct SwapChainEGL : public SwapChain { + SwapChainEGL(PlatformEGL const& platform, void* nativeWindow, uint64_t flags); + SwapChainEGL(PlatformEGL const& platform, uint32_t width, uint32_t height, uint64_t flags); + void terminate(PlatformEGL& platform); + EGLSurface sur = EGL_NO_SURFACE; Config attribs{}; EGLNativeWindowType nativeWindow{}; @@ -188,10 +188,31 @@ class PlatformEGL : public OpenGLPlatform { void initializeGlExtensions() noexcept; -protected: - EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const; + struct ExternalImageEGL : public ExternalImage { + EGLImageKHR eglImage = EGL_NO_IMAGE; + protected: + ~ExternalImageEGL() override; + }; private: + // prevent derived classes' implementations to call through + [[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override; + [[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override; + void destroySwapChain(SwapChain* swapChain) noexcept override; + + EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const; + + EGLDisplay mEGLDisplay = EGL_NO_DISPLAY; + EGLContext mEGLContext = EGL_NO_CONTEXT; + EGLContext mEGLContextProtected = EGL_NO_CONTEXT; + EGLSurface mEGLDummySurface = EGL_NO_SURFACE; + ContextType mCurrentContextType = ContextType::NONE; + // mEGLConfig is valid only if ext.egl.KHR_no_config_context is false + EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR; + Config mContextAttribs; + std::vector mAdditionalContexts; + bool mMSAA4XSupport = false; + class EGL { EGLDisplay& mEGLDisplay; EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE; @@ -200,12 +221,14 @@ class PlatformEGL : public OpenGLPlatform { public: explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {} EGLBoolean makeCurrent(EGLContext context, - EGLSurface drawSurface, EGLSurface readSurface) noexcept; + EGLSurface drawSurface, EGLSurface readSurface); - EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept { + EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) { return makeCurrent(mCurrentContext, drawSurface, readSurface); } } egl{ mEGLDisplay }; + + bool checkIfMSAASwapChainSupported(uint32_t samples) const noexcept; }; } // namespace filament::backend diff --git a/package/android/libs/filament/include/backend/platforms/PlatformEGLAndroid.h b/package/android/libs/filament/include/backend/platforms/PlatformEGLAndroid.h index 32f83038..9410a681 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformEGLAndroid.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformEGLAndroid.h @@ -17,11 +17,21 @@ #ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H #define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H +#include "AndroidNdk.h" + #include +#include #include #include #include +#include +#include + +#include + +#include + #include #include @@ -33,13 +43,45 @@ class ExternalStreamManagerAndroid; * A concrete implementation of OpenGLPlatform and subclass of PlatformEGL that supports * EGL on Android. It adds Android streaming functionality to PlatformEGL. */ -class PlatformEGLAndroid : public PlatformEGL { +class PlatformEGLAndroid : public PlatformEGL, public AndroidNdk { public: PlatformEGLAndroid() noexcept; ~PlatformEGLAndroid() noexcept override; + /** + * Creates an ExternalImage from a EGLImageKHR + */ + ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept; + + struct UTILS_PUBLIC ExternalImageDescAndroid { + uint32_t width; // Texture width + uint32_t height; // Texture height + TextureFormat format;// Texture format + TextureUsage usage; // Texture usage flags + }; + + ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept; + + /** + * Converts a sync to an external file descriptor, if possible. Accepts an + * opaque handle to a sync, as well as a pointer to where the fd should be + * stored. + * @param sync The sync to be converted to a file descriptor. + * @param fd A pointer to where the file descriptor should be stored. + * @return `true` on success, `false` on failure. The default implementation + * returns `false`. + */ + bool convertSyncToFd(Sync* sync, int* fd) noexcept; + protected: + struct { + struct { + bool ANDROID_presentation_time = false; + bool ANDROID_get_frame_timestamps = false; + bool ANDROID_native_fence_sync = false; + } egl; + } ext; // -------------------------------------------------------------------------------------------- // Platform Interface @@ -51,25 +93,48 @@ class PlatformEGLAndroid : public PlatformEGL { int getOSVersion() const noexcept override; Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; + + bool isCompositorTimingSupported() const noexcept override; + + bool queryCompositorTiming(SwapChain const* swapchain, + CompositorTiming* outCompositorTiming) const noexcept override; + + bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override; + + bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId, + FrameTimestamps* outFrameTimestamps) const noexcept override; // -------------------------------------------------------------------------------------------- // OpenGLPlatform Interface + struct SyncEGLAndroid : public Sync { + EGLSyncKHR sync; + }; + void terminate() noexcept override; + void beginFrame( + int64_t monotonic_clock_ns, + int64_t refreshIntervalNs, + uint32_t frameId) noexcept override; + + void preCommit() noexcept override; + /** * Set the presentation time using `eglPresentationTimeANDROID` * @param presentationTimeInNanosecond */ void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept override; - Stream* createStream(void* nativeStream) noexcept override; void destroyStream(Stream* stream) noexcept override; + Sync* createSync() noexcept override; + void destroySync(Sync* sync) noexcept override; void attach(Stream* stream, intptr_t tname) noexcept override; void detach(Stream* stream) noexcept override; void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override; + math::mat3f getTransformMatrix(Stream* stream) noexcept override; /** * Converts a AHardwareBuffer to EGLImage @@ -78,9 +143,61 @@ class PlatformEGLAndroid : public PlatformEGL { */ AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; + + struct ExternalImageEGLAndroid : public ExternalImageEGL { + AHardwareBuffer* aHardwareBuffer = nullptr; + uint32_t width; // Texture width + uint32_t height; // Texture height + TextureFormat format;// Texture format + TextureUsage usage; // Texture usage flags + bool sRGB = false; + + protected: + ~ExternalImageEGLAndroid() override; + }; + + bool setExternalImage(ExternalImageHandleRef externalImage, + ExternalTexture* texture) noexcept override; + bool setImage(ExternalImageEGLAndroid const* eglExternalImage, + ExternalTexture* texture) noexcept; + + bool makeCurrent(ContextType type, + SwapChain* drawSwapChain, + SwapChain* readSwapChain) override; + private: + struct SwapChainEGLAndroid; + struct AndroidDetails; + + // prevent derived classes' implementations to call through + [[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override; + [[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override; + void destroySwapChain(SwapChain* swapChain) noexcept override; + + bool isProducerThrottlingControlSupported() const; + + int32_t setProducerThrottlingEnabled(EGLNativeWindowType nativeWindow, bool enabled) const; + + struct InitializeJvmForPerformanceManagerIfNeeded { + InitializeJvmForPerformanceManagerIfNeeded(); + }; + + struct ExternalTextureAndroid : public ExternalTexture { + EGLImageKHR eglImage = EGL_NO_IMAGE; + }; + int mOSVersion; ExternalStreamManagerAndroid& mExternalStreamManager; + AndroidDetails& mAndroidDetails; + InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded; + utils::PerformanceHintManager mPerformanceHintManager; + utils::PerformanceHintManager::Session mPerformanceHintSession; + using clock = std::chrono::high_resolution_clock; + clock::time_point mStartTimeOfActualWork; + SwapChainEGLAndroid* mCurrentDrawSwapChain{}; + bool mAssertNativeWindowIsValid = false; }; } // namespace filament::backend diff --git a/package/android/libs/filament/include/backend/platforms/PlatformEGLHeadless.h b/package/android/libs/filament/include/backend/platforms/PlatformEGLHeadless.h index 40d285b9..300748e0 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformEGLHeadless.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformEGLHeadless.h @@ -29,7 +29,7 @@ class PlatformEGLHeadless : public PlatformEGL { PlatformEGLHeadless() noexcept; Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const Platform::DriverConfig& driverConfig) override; protected: bool isOpenGL() const noexcept override; diff --git a/package/android/libs/filament/include/backend/platforms/PlatformGLX.h b/package/android/libs/filament/include/backend/platforms/PlatformGLX.h index 796e27a1..8d0eda33 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformGLX.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformGLX.h @@ -26,7 +26,10 @@ #include +#include +#include #include +#include namespace filament::backend { @@ -39,7 +42,7 @@ class PlatformGLX : public OpenGLPlatform { // Platform Interface Driver* createDriver(void* sharedGLContext, - const DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; int getOSVersion() const noexcept final override { return 0; } @@ -48,18 +51,26 @@ class PlatformGLX : public OpenGLPlatform { void terminate() noexcept override; + bool isExtraContextSupported() const noexcept override; + void createContext(bool shared) override; + void releaseContext() noexcept override; + SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; private: - Display *mGLXDisplay; - GLXContext mGLXContext; - GLXFBConfig* mGLXConfig; + Display* mGLXDisplay; + GLXContext mGLXContext{}; + GLXFBConfig mGLXConfig{}; GLXPbuffer mDummySurface; std::vector mPBuffers; + + // Variables for shared contexts + std::unordered_map mAdditionalContexts; + std::shared_mutex mAdditionalContextsLock; }; } // namespace filament::backend diff --git a/package/android/libs/filament/include/backend/platforms/PlatformMetal-ObjC.h b/package/android/libs/filament/include/backend/platforms/PlatformMetal-ObjC.h new file mode 100644 index 00000000..2635ea67 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/PlatformMetal-ObjC.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 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_PRIVATE_PLATFORMMETAL_OBJC_H +#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H + +#import + +namespace filament::backend { + +struct MetalDevice { + id device; +}; + +struct MetalCommandQueue { + id commandQueue; +}; + +struct MetalCommandBuffer { + id commandBuffer; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H diff --git a/package/android/libs/filament/include/backend/platforms/PlatformMetal.h b/package/android/libs/filament/include/backend/platforms/PlatformMetal.h new file mode 100644 index 00000000..a2e3ac25 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/PlatformMetal.h @@ -0,0 +1,118 @@ +/* + * 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_PRIVATE_PLATFORMMETAL_H +#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H + +#include +#include + +namespace filament::backend { + +struct PlatformMetalImpl; + +// In order for this header to be compatible with Objective-C and C++, we use these wrappers around +// id objects. +// See PlatformMetal-Objc.h. +struct MetalDevice; +struct MetalCommandQueue; +struct MetalCommandBuffer; + +class PlatformMetal final : public Platform { +public: + PlatformMetal(); + ~PlatformMetal() noexcept override; + + Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override; + int getOSVersion() const noexcept override { return 0; } + + /** + * Optionally initializes the Metal platform by acquiring resources necessary for rendering. + * + * This method attempts to acquire a Metal device and command queue, returning true if both are + * successfully obtained, or false otherwise. Typically, these objects are acquired when + * the Metal backend is initialized. This method allows clients to check for their availability + * earlier. + * + * Calling initialize() is optional and safe to do so multiple times. After initialize() returns + * true, subsequent calls will continue to return true but have no effect. + * + * initialize() must be called from the main thread. + * + * @returns true if the device and command queue have been successfully obtained; false + * otherwise. + */ + bool initialize() noexcept; + + /** + * Obtain the preferred Metal device object for the backend to use. + * + * On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is + * free to decide which one to use. On mobile systems with a single GPU, implementations should + * simply return the result of MTLCreateSystemDefaultDevice(); + * + * createDevice is called by the Metal backend from the backend thread. + */ + virtual void createDevice(MetalDevice& outDevice) noexcept; + + /** + * Create a command submission queue on the Metal device object. + * + * createCommandQueue is called by the Metal backend from the backend thread. + * + * @param device The device which was returned from createDevice() + */ + virtual void createCommandQueue( + MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept; + + /** + * Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is + * guaranteed to execute before all subsequent command buffers created either by Filament, or + * further calls to this method. + * + * createAndEnqueueCommandBuffer must be called from the main thread. + */ + void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept; + + /** + * The action to take if a Drawable cannot be acquired. + * + * Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the + * completion of each frame. These are limited and provided round-robin style by the system. + * + * setDrawableFailureBehavior must be called from the main thread. + */ + enum class DrawableFailureBehavior : uint8_t { + /** + * Terminates the application and reports an error message (default). + */ + PANIC, + /* + * Aborts execution of the current frame. The Metal backend will attempt to acquire a new + * drawable at the next frame. + */ + ABORT_FRAME + }; + void setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept; + DrawableFailureBehavior getDrawableFailureBehavior() const noexcept; + +private: + PlatformMetalImpl* pImpl = nullptr; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H diff --git a/package/android/libs/filament/include/backend/platforms/PlatformOSMesa.h b/package/android/libs/filament/include/backend/platforms/PlatformOSMesa.h new file mode 100644 index 00000000..10e1fb18 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/PlatformOSMesa.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 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_OPENGL_OPENGL_PLATFORM_OSMESA_H +#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H + +#include + +#include "bluegl/BlueGL.h" + +#if defined(__linux__) +#include +#elif defined(__APPLE__) +#undef GLAPI +#include +#endif + +#include +#include + +namespace filament::backend { + +/** + * A concrete implementation of OpenGLPlatform that uses OSMesa, which is an offscreen + * context that can be used in conjunction with Mesa for software rasterization. + * See https://docs.mesa3d.org/osmesa.html for more information. + */ +class PlatformOSMesa : public OpenGLPlatform { +protected: + // -------------------------------------------------------------------------------------------- + // Platform Interface + + Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override; + + int getOSVersion() const noexcept final override { return 0; } + + // -------------------------------------------------------------------------------------------- + // OpenGLPlatform Interface + + void terminate() noexcept override; + + SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; + SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; + void destroySwapChain(SwapChain* swapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, + SwapChain* readSwapChain) override; + void commit(SwapChain* swapChain) noexcept override; + +private: + OSMesaContext mContext; + void* mOsMesaApi = nullptr; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H diff --git a/package/android/libs/filament/include/backend/platforms/PlatformWGL.h b/package/android/libs/filament/include/backend/platforms/PlatformWGL.h index e0003156..49c79812 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformWGL.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformWGL.h @@ -38,7 +38,7 @@ class PlatformWGL : public OpenGLPlatform { // Platform Interface Driver* createDriver(void* sharedGLContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const Platform::DriverConfig& driverConfig) override; int getOSVersion() const noexcept final override { return 0; } @@ -53,7 +53,7 @@ class PlatformWGL : public OpenGLPlatform { SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; protected: @@ -61,8 +61,12 @@ class PlatformWGL : public OpenGLPlatform { HWND mHWnd = NULL; HDC mWhdc = NULL; PIXELFORMATDESCRIPTOR mPfd = {}; - std::vector mAdditionalContexts; std::vector mAttribs; + + // For shared contexts + static constexpr int SHARED_CONTEXT_NUM = 2; + std::vector mAdditionalContexts; + std::atomic mNextFreeSharedContextIndex{0}; }; } // namespace filament::backend diff --git a/package/android/libs/filament/include/backend/platforms/PlatformWebGL.h b/package/android/libs/filament/include/backend/platforms/PlatformWebGL.h index 0d83fbb9..21083f58 100644 --- a/package/android/libs/filament/include/backend/platforms/PlatformWebGL.h +++ b/package/android/libs/filament/include/backend/platforms/PlatformWebGL.h @@ -33,8 +33,7 @@ class PlatformWebGL : public OpenGLPlatform { // -------------------------------------------------------------------------------------------- // Platform Interface - Driver* createDriver(void* sharedGLContext, - const Platform::DriverConfig& driverConfig) noexcept override; + Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override; int getOSVersion() const noexcept override; @@ -43,10 +42,10 @@ class PlatformWebGL : public OpenGLPlatform { void terminate() noexcept override; - SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; + SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; }; diff --git a/package/android/libs/filament/include/backend/platforms/VulkanPlatform.h b/package/android/libs/filament/include/backend/platforms/VulkanPlatform.h index 6201d644..edfdea29 100644 --- a/package/android/libs/filament/include/backend/platforms/VulkanPlatform.h +++ b/package/android/libs/filament/include/backend/platforms/VulkanPlatform.h @@ -17,15 +17,20 @@ #ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H #define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H +#include +#include #include #include #include #include +#include #include -#include +#include +#include +#include #include #include @@ -41,12 +46,24 @@ using SwapChain = Platform::SwapChain; */ struct VulkanPlatformPrivate; +// Forward declare the fence status that will be maintained by the command +// buffer manager. +struct VulkanCmdFence; + /** * A Platform interface that creates a Vulkan backend. */ class VulkanPlatform : public Platform, utils::PrivateImplementation { public: + struct ExtensionHashFn { + std::size_t operator()(utils::CString const& s) const noexcept { + return std::hash{}(s.data()); + } + }; + // Utility for managing device or instance extensions during initialization. + using ExtensionSet = std::unordered_set; + /** * A collection of handles to objects and metadata that comprises a Vulkan context. The client * can instantiate this struct and pass to Engine::Builder::sharedContext if they wishes to @@ -63,6 +80,9 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation fenceStatus) noexcept; + + /** + * Destroys a sync. If called with a sync not created by this platform + * object, this will lead to undefined behavior. + * + * @param sync The sync to destroy, which was created by this platform + * instance. + */ + virtual void destroySync(Platform::Sync* sync) noexcept; + + /** + * Allows implementers to provide instance extensions that they'd like to include in the + * instance creation. + * @return A set of extensions to enable for the instance. + */ + virtual ExtensionSet getRequiredInstanceExtensions() { return {}; } + /** * Destroy the swapchain. * @param handle The handle returned by createSwapChain() @@ -235,14 +303,226 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation; - static ExtensionSet getRequiredInstanceExtensions(); + /** + * @return The family index of the protected graphics queue selected for the + * Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept; + + /** + * @return The index of the protected graphics queue (if there are multiple + * graphics queues) selected for the Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueIndex() const noexcept; + + /** + * @return The protected queue that was selected for the Vulkan backend. + */ + VkQueue getProtectedGraphicsQueue() const noexcept; + + struct ExternalImageMetadata { + /** + * The Filament texture format. + */ + TextureFormat filamentFormat; + + /** + * The Filament texture usage. + */ + TextureUsage filamentUsage; + + /** + * The width of the external image + */ + uint32_t width; + + /** + * The height of the external image + */ + uint32_t height; + + /** + * The layer count of the external image + */ + uint32_t layers; + + /** + * The numbers of samples per texel + */ + VkSampleCountFlagBits samples; + + /** + * The format of the external image + */ + VkFormat format; + + /** + * The type of external format (opaque int) if used. + */ + uint64_t externalFormat; + + /** + * Image usage + */ + VkImageUsageFlags usage; + + /** + * Allocation size + */ + VkDeviceSize allocationSize; + + /** + * Heap information + */ + uint32_t memoryTypeBits; + + /** + * Ycbcr conversion components + */ + VkComponentMapping ycbcrConversionComponents; + + /** + * Ycbcr model + */ + VkSamplerYcbcrModelConversion ycbcrModel; + + /** + * Ycbcr range + */ + VkSamplerYcbcrRange ycbcrRange; + + /** + * Ycbcr x chroma offset + */ + VkChromaLocation xChromaOffset; + + /** + * Ycbcr y chroma offset + */ + VkChromaLocation yChromaOffset; + }; + + + // Note that the image metadata might change per-frame, hence we need a method for extracting + // it. + virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const { + return {}; + } + + struct ImageData { + struct Bundle { + VkImage image = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + + inline bool valid() const noexcept { + return image != VK_NULL_HANDLE; + } + }; + // It's possible for the external image to also have a known VK format. We need to create an + // image for that in case we are not looking to use an external "sampler" with this image. + Bundle internal; + + // If we get a externalFormat in the metadata, then we should create an image with + // VK_FORMAT_UNDEFINED + Bundle external; + }; + + virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const { + return {}; + } + +protected: + struct VulkanSync : public Platform::Sync { + VkFence fence; + std::shared_ptr fenceStatus; + }; + + /** + * Creates the VkInstance used by Filament's Vulkan backend. + * + * This method can be overridden in subclasses to customize VkInstance creation, such as + * adding application-specific layers or extensions. + * + * The provided `createInfo` contains layers and extensions required by Filament. + * If you override this method and need to modify the `createInfo` struct, you must first + * make a copy of it and modify the copy. + * + * @param createInfo The VkInstanceCreateInfo prepared by Filament. + * @return The created VkInstance, or VK_NULL_HANDLE on failure. + */ + virtual VkInstance createVkInstance(VkInstanceCreateInfo const& createInfo) noexcept; + + /** + * Selects a VkPhysicalDevice (GPU) for Filament's Vulkan backend to use. + * + * This method can be overridden in subclasses to implement custom GPU selection logic. + * For example, an application might override this to prefer a discrete GPU over an + * integrated one based on device properties. + * + * The default implementation selects the first device that meets Filament's requirements. + * + * @param instance The VkInstance to enumerate devices from. + * @return The selected VkPhysicalDevice, or VK_NULL_HANDLE if no suitable device is found. + */ + virtual VkPhysicalDevice selectVkPhysicalDevice(VkInstance instance) noexcept; + + /** + * Creates the VkDevice used by Filament's Vulkan backend. + * + * This method can be overridden in subclasses to customize VkDevice creation, such as + * adding application-specific extensions or enabling features. + * + * The provided `createInfo` contains extensions and features required by Filament. + * If you override this method and need to modify the `createInfo` struct, you must first + * make a copy of it and modify the copy. + * + * @param createInfo The VkDeviceCreateInfo prepared by Filament. + * @return The created VkDevice, or VK_NULL_HANDLE on failure. + */ + virtual VkDevice createVkDevice(VkDeviceCreateInfo const& createInfo) noexcept; using SurfaceBundle = std::tuple; - static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, - uint64_t flags) noexcept; + virtual ExtensionSet getSwapchainInstanceExtensions() const = 0; + virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept = 0; + + virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept; + + /** + * Query if transient attachments are supported by the backend. + */ + bool isTransientAttachmentSupported() const noexcept; + +private: + /** + * Contains information about features that should be requested + * when calling vkCreateDevice, based on feature support from + * vkGetPhysicalDeviceFeatures2. + */ + struct MiscDeviceFeatures { + /** + * This allows creation of a VkGraphicsPipeline without a + * render pass specified. + */ + bool dynamicRendering; + + /** + * Allows creation of a 2d image view, or 2d image view array, + * to be created from a 3d VkImage. + */ + bool imageView2Don3DImage; + }; + + void createInstance(ExtensionSet const& requiredExts) noexcept; + + void queryAndSetDeviceFeatures(Platform::DriverConfig const& driverConfig, + ExtensionSet const& instExts, ExtensionSet const& deviceExts, + void* sharedContext) noexcept; + + void createLogicalDeviceAndQueues(ExtensionSet const& deviceExtensions, + VkPhysicalDeviceFeatures const& features, + VkPhysicalDeviceVulkan11Features const& vk11Features, bool createProtectedQueue, + MiscDeviceFeatures const& requestedFeatures) noexcept; friend struct VulkanPlatformPrivate; }; diff --git a/package/android/libs/filament/include/backend/platforms/VulkanPlatformAndroid.h b/package/android/libs/filament/include/backend/platforms/VulkanPlatformAndroid.h new file mode 100644 index 00000000..ba82db81 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/VulkanPlatformAndroid.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_VULKAN_PLATFORM_ANDROID_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H + +#include "AndroidNdk.h" + +#include +#include + +#include + +#include + +namespace filament::backend { + +class VulkanPlatformAndroid : public VulkanPlatform, public AndroidNdk { +public: + ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, + bool sRGB) noexcept; + + struct UTILS_PUBLIC ExternalImageDescAndroid { + uint32_t width; // Texture width + uint32_t height; // Texture height + TextureFormat format;// Texture format + TextureUsage usage; // Texture usage flags + }; + + VulkanPlatformAndroid(); + + ~VulkanPlatformAndroid() noexcept override; + + ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc( + ExternalImageHandleRef externalImage) const noexcept; + + ExternalImageMetadata extractExternalImageMetadata( + ExternalImageHandleRef image) const override; + + ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override; + + /** + * Converts a sync to an external file descriptor, if possible. Accepts an + * opaque handle to a sync, as well as a pointer to where the fd should be + * stored. + * @param sync The sync to be converted to a file descriptor. + * @param fd A pointer to where the file descriptor should be stored. + * @return `true` on success, `false` on failure. The default implementation + * returns `false`. + */ + bool convertSyncToFd(Sync* sync, int* fd) const noexcept; + + int getOSVersion() const noexcept override; + + void terminate() override; + + Driver* createDriver(void* sharedContext, + DriverConfig const& driverConfig) override; + + + bool isCompositorTimingSupported() const noexcept override; + + bool queryCompositorTiming(SwapChain const* swapchain, + CompositorTiming* outCompositorTiming) const noexcept override; + + bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override; + + bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId, + FrameTimestamps* outFrameTimestamps) const noexcept override; + + +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + + using SurfaceBundle = SurfaceBundle; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; + + VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override; + +private: + struct AndroidDetails; + + struct ExternalImageVulkanAndroid : public ExternalImage { + AHardwareBuffer* aHardwareBuffer = nullptr; + bool sRGB = false; + + protected: + ~ExternalImageVulkanAndroid() override; + }; + + AndroidDetails& mAndroidDetails; + int mOSVersion{}; +}; + +}// namespace filament::backend + +#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H diff --git a/package/android/libs/filament/include/backend/platforms/VulkanPlatformApple.h b/package/android/libs/filament/include/backend/platforms/VulkanPlatformApple.h new file mode 100644 index 00000000..5a7c2119 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/VulkanPlatformApple.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_VULKANPLATFORMAPPLE_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H + +#include + +namespace filament::backend { + +class VulkanPlatformApple : public VulkanPlatform { +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H diff --git a/package/android/libs/filament/include/backend/platforms/VulkanPlatformLinux.h b/package/android/libs/filament/include/backend/platforms/VulkanPlatformLinux.h new file mode 100644 index 00000000..2fb964dd --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/VulkanPlatformLinux.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_VULKANPLATFORMLINUX_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H + +#include + +namespace filament::backend { + +class VulkanPlatformLinux : public VulkanPlatform { +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H diff --git a/package/android/libs/filament/include/backend/platforms/VulkanPlatformWindows.h b/package/android/libs/filament/include/backend/platforms/VulkanPlatformWindows.h new file mode 100644 index 00000000..0b55dede --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/VulkanPlatformWindows.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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_PLATFORMS_VULKANPLATFORMWINDOWS_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H + +#include + +namespace filament::backend { + +class VulkanPlatformWindows : public VulkanPlatform { +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H diff --git a/package/android/libs/filament/include/backend/platforms/WebGPUPlatform.h b/package/android/libs/filament/include/backend/platforms/WebGPUPlatform.h new file mode 100644 index 00000000..ebdb70ac --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/WebGPUPlatform.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORM_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H + +#include + +#if defined(__linux__) && defined(FILAMENT_SUPPORTS_X11) +// Resolve the conflicts between webgpu_cpp.h and X11 defines +#undef Always +#undef Success +#undef None +#undef True +#undef False +#undef Status +#undef Bool +#endif +#include + +#include +#include + +namespace filament::backend { + +/** + * A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU + * driver (backend). + */ +class WebGPUPlatform : public Platform { +public: + WebGPUPlatform(); + ~WebGPUPlatform() override = default; + + [[nodiscard]] int getOSVersion() const noexcept final { return 0; } + + [[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; } + + // TODO consider that this functionality is not WebGPU-specific, and thus could be + // placed in a generic place and even reused across backends. Alternatively, + // a 3rd party library could be considered. However, this was a simple and + // quick change and works for now. + // gets the size (height and width) of the surface/window + [[nodiscard]] virtual wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const = 0; + // either returns a valid surface or panics + [[nodiscard]] virtual wgpu::Surface createSurface(void* nativeWindow, uint64_t flags) = 0; + // either returns a valid adapter or panics + [[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface); + // either returns a valid device or panics + [[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter); + + struct Configuration { + wgpu::BackendType forceBackendType = wgpu::BackendType::Undefined; + }; + + [[nodiscard]] virtual Configuration getConfiguration() const noexcept { + return {}; + } + +protected: + [[nodiscard]] Driver* createDriver(void* sharedContext, + const Platform::DriverConfig& driverConfig) override; + + // returns adapter request option variations applicable for the particular + // platform + [[nodiscard]] virtual std::vector getAdapterOptions() = 0; + + // we may consider having the driver own this in the future + wgpu::Instance mInstance; +}; + +}// namespace filament::backend + +#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H diff --git a/package/android/libs/filament/include/backend/platforms/WebGPUPlatformAndroid.h b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformAndroid.h new file mode 100644 index 00000000..fc3f33b1 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformAndroid.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMANDROID_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H + +#include + +namespace filament::backend { + +class WebGPUPlatformAndroid : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H diff --git a/package/android/libs/filament/include/backend/platforms/WebGPUPlatformApple.h b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformApple.h new file mode 100644 index 00000000..81f9f089 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformApple.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMAPPLE_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H + +#include + +namespace filament::backend { + +class WebGPUPlatformApple : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H diff --git a/package/android/libs/filament/include/backend/platforms/WebGPUPlatformLinux.h b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformLinux.h new file mode 100644 index 00000000..43ad7af6 --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformLinux.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMLINUX_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H + +#include + +namespace filament::backend { + +class WebGPUPlatformLinux : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H diff --git a/package/android/libs/filament/include/backend/platforms/WebGPUPlatformWindows.h b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformWindows.h new file mode 100644 index 00000000..cc7f6d9a --- /dev/null +++ b/package/android/libs/filament/include/backend/platforms/WebGPUPlatformWindows.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMWINDOWS_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H + +#include + +namespace filament::backend { + +class WebGPUPlatformWindows : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H diff --git a/package/android/libs/filament/include/camutils/Manipulator.h b/package/android/libs/filament/include/camutils/Manipulator.h index 2cc8a4da..5ea49eaf 100644 --- a/package/android/libs/filament/include/camutils/Manipulator.h +++ b/package/android/libs/filament/include/camutils/Manipulator.h @@ -109,6 +109,7 @@ class CAMUTILS_PUBLIC Manipulator { vec4 groundPlane; RayCallback raycastCallback; void* raycastUserdata; + bool panning = true; }; struct Builder { @@ -143,6 +144,8 @@ class CAMUTILS_PUBLIC Manipulator { Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan + Builder& panning(bool enabled); //! Sets whether panning is enabled + /** * Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT. * diff --git a/package/android/libs/filament/include/filamat/Enums.h b/package/android/libs/filament/include/filamat/Enums.h index 04b5ff8b..3a28bc5b 100644 --- a/package/android/libs/filament/include/filamat/Enums.h +++ b/package/android/libs/filament/include/filamat/Enums.h @@ -35,6 +35,7 @@ using OutputTarget = MaterialBuilder::OutputTarget; using OutputQualifier = MaterialBuilder::VariableQualifier; using OutputType = MaterialBuilder::OutputType; using ConstantType = MaterialBuilder::ConstantType; +using ShaderStageType = MaterialBuilder::ShaderStageFlags; // Convenience methods to convert std::string to Enum and also iterate over Enum values. class Enums { @@ -79,6 +80,7 @@ class Enums { static std::unordered_map mStringToOutputQualifier; static std::unordered_map mStringToOutputType; static std::unordered_map mStringToConstantType; + static std::unordered_map mStringToShaderStageType; }; template diff --git a/package/android/libs/filament/include/filamat/IncludeCallback.h b/package/android/libs/filament/include/filamat/IncludeCallback.h deleted file mode 100644 index 659ba289..00000000 --- a/package/android/libs/filament/include/filamat/IncludeCallback.h +++ /dev/null @@ -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 - -#include - -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; - -} // namespace filamat - -#endif diff --git a/package/android/libs/filament/include/filamat/MaterialBuilder.h b/package/android/libs/filament/include/filamat/MaterialBuilder.h index 4b66965d..e8b3ea78 100644 --- a/package/android/libs/filament/include/filamat/MaterialBuilder.h +++ b/package/android/libs/filament/include/filamat/MaterialBuilder.h @@ -21,7 +21,6 @@ #include -#include #include #include @@ -35,11 +34,14 @@ #include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -80,7 +82,12 @@ class UTILS_PUBLIC MaterialBuilderBase { OPENGL = 0x01u, VULKAN = 0x02u, METAL = 0x04u, + WEBGPU = 0x08u, +#ifdef FILAMENT_SUPPORTS_WEBGPU + ALL = OPENGL | VULKAN | METAL | WEBGPU +#else ALL = OPENGL | VULKAN | METAL +#endif }; /* @@ -110,6 +117,11 @@ class UTILS_PUBLIC MaterialBuilderBase { PERFORMANCE }; + enum class Workarounds : uint64_t { + NONE = 0, + ALL = 0xFFFFFFFFFFFFFFFF + }; + /** * Initialize MaterialBuilder. * @@ -133,7 +145,9 @@ class UTILS_PUBLIC MaterialBuilderBase { Platform mPlatform = Platform::DESKTOP; TargetApi mTargetApi = (TargetApi) 0; Optimization mOptimization = Optimization::PERFORMANCE; + Workarounds mWorkarounds = Workarounds::ALL; bool mPrintShaders = false; + bool mSaveRawVariants = false; bool mGenerateDebugInfo = false; bool mIncludeEssl1 = true; utils::bitset32 mShaderModels; @@ -162,6 +176,7 @@ inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend( case Backend::OPENGL: return TargetApi::OPENGL; case Backend::VULKAN: return TargetApi::VULKAN; case Backend::METAL: return TargetApi::METAL; + case Backend::WEBGPU: return TargetApi::WEBGPU; case Backend::NOOP: return TargetApi::OPENGL; } } @@ -210,12 +225,13 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { MaterialBuilder(MaterialBuilder&& rhs) noexcept = default; MaterialBuilder& operator=(MaterialBuilder&& rhs) noexcept = default; - static constexpr size_t MATERIAL_VARIABLES_COUNT = 4; + static constexpr size_t MATERIAL_VARIABLES_COUNT = 5; enum class Variable : uint8_t { CUSTOM0, CUSTOM1, CUSTOM2, - CUSTOM3 + CUSTOM3, + CUSTOM4, // CUSTOM4 is only available if the vertex attribute `color` is not required. // when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT }; @@ -237,6 +253,7 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { using AttributeType = filament::backend::UniformType; using UniformType = filament::backend::UniformType; using ConstantType = filament::backend::ConstantType; + using ConstantValue = filament::backend::ConstantValue; using SamplerType = filament::backend::SamplerType; using SubpassType = filament::backend::SubpassType; using SamplerFormat = filament::backend::SamplerFormat; @@ -245,6 +262,8 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { using CullingMode = filament::backend::CullingMode; using FeatureLevel = filament::backend::FeatureLevel; using StereoscopicType = filament::backend::StereoscopicType; + using ShaderStage = filament::backend::ShaderStage; + using ShaderStageFlags = filament::backend::ShaderStageFlags; enum class VariableQualifier : uint8_t { OUT @@ -283,6 +302,9 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { //! Set the file name of this material file. Used in error reporting. MaterialBuilder& fileName(const char* name) noexcept; + //! Set the commandline parameters of matc. Used for debugging purpose. + MaterialBuilder& compilationParameters(const char* params) noexcept; + //! Set the shading model. MaterialBuilder& shading(Shading shading) noexcept; @@ -295,7 +317,7 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { //! Add a parameter array to this material. MaterialBuilder& parameter(const char* name, size_t size, UniformType type, - ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept; + ParameterPrecision precision = ParameterPrecision::DEFAULT); //! Add a constant parameter to this material. template @@ -314,13 +336,18 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { MaterialBuilder& parameter(const char* name, SamplerType samplerType, SamplerFormat format = SamplerFormat::FLOAT, ParameterPrecision precision = ParameterPrecision::DEFAULT, - bool multisample = false) noexcept; + bool filterable = true, /* defaulting to filterable because format is default to float */ + bool multisample = false, const char* transformName = "", + std::optional stages = {}); - MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept; + MaterialBuilder& buffer(filament::BufferInterfaceBlock bib); //! Custom variables (all float4). MaterialBuilder& variable(Variable v, const char* name) noexcept; + MaterialBuilder& variable(Variable v, const char* name, + ParameterPrecision precision) noexcept; + /** * Require a specified attribute. * @@ -356,18 +383,13 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { * } * ~~~~~ * - * @param code The source code of the material. + * @param code The source code of the material. Expected it to be all inlined. (#includes are + * resolved.) * @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. * @@ -391,7 +413,8 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { * } * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * @param code The source code of the material. + * @param code The source code of the material. Expected it to be all inlined. (#includes are + * resolved.) * @param line The line number offset of the material, where 0 is the first line. Used for error * reporting */ @@ -515,6 +538,12 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { //! Enable / disable flipping of the Y coordinate of UV attributes, enabled by default. MaterialBuilder& flipUV(bool flipUV) noexcept; + //! Enable / disable the cheapest linear fog, disabled by default. + MaterialBuilder& linearFog(bool enabled) noexcept; + + //! Enable / disable shadow far attenuation, enabled by default. + MaterialBuilder& shadowFarAttenuation(bool enabled) noexcept; + //! Enable / disable multi-bounce ambient occlusion, disabled by default on mobile. MaterialBuilder& multiBounceAmbientOcclusion(bool multiBounceAO) noexcept; @@ -580,11 +609,25 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { */ MaterialBuilder& optimization(Optimization optimization) noexcept; - // TODO: this is present here for matc's "--print" flag, but ideally does not belong inside - // MaterialBuilder. + /** + * Specifies workarounds to enable during code generation. By default, all workaround are + * enabled. These workarounds typically disable important optimizations and in some cases + * whole features. + */ + MaterialBuilder& workarounds(Workarounds workarounds) 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, this will write the raw generated GLSL for each variant to a text file in the + * current directory. The file will be named after the material name and the variant name. Its + * extension will be derived from the shader stage. For example, mymaterial_0x0e.frag, + * mymaterial_0x18.vert, etc. + */ + MaterialBuilder& saveRawVariants(bool saveRawVariants) noexcept; + //! If true, will include debugging information in generated SPIRV. MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept; @@ -596,7 +639,7 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { //! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain. MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target, Precision precision, - OutputType type, const char* name, int location = -1) noexcept; + OutputType type, const char* name, int location = -1); MaterialBuilder& enableFramebufferFetch() noexcept; @@ -611,11 +654,26 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { //! specify compute kernel group size MaterialBuilder& groupSize(filament::math::uint3 groupSize) noexcept; + /** + * Force Filament to use its default variant for depth passes. Useful if a material provides a + * custom vertex shader which can be skipped during depth-only passes. + */ + MaterialBuilder& useDefaultDepthVariant() noexcept; + + /** + * Sets the source ASCII material (aka .mat file). + * The provided `source` string_view must remain valid until MaterialBuilder::build() is called. + */ + MaterialBuilder& materialSource(std::string_view source) noexcept; + + //! Set the (client requested) api level that the material is supposed to be compiled against. + MaterialBuilder& setApiLevel(uint32_t apiLevel) 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; + Package build(utils::JobSystem& jobSystem); public: // The methods and types below are for internal use @@ -625,27 +683,51 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { * Add a subpass parameter to this material. */ MaterialBuilder& subpass(SubpassType subpassType, - SamplerFormat format, ParameterPrecision precision, const char* name) noexcept; + SamplerFormat format, ParameterPrecision precision, const char* name); MaterialBuilder& subpass(SubpassType subpassType, - SamplerFormat format, const char* name) noexcept; + SamplerFormat format, const char* name); MaterialBuilder& subpass(SubpassType subpassType, - ParameterPrecision precision, const char* name) noexcept; - MaterialBuilder& subpass(SubpassType subpassType, const char* name) noexcept; + ParameterPrecision precision, const char* name); + MaterialBuilder& subpass(SubpassType subpassType, const char* name); struct Parameter { Parameter() noexcept: parameterType(INVALID) {} // Sampler - Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms) - : name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms) { } + Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, + bool filterable, bool ms, const char* tn, std::optional s) + : name(paramName), + size(1), + precision(p), + samplerType(t), + format(f), + filterable(filterable), + multisample(ms), + transformName(tn), + stages(s), + parameterType(SAMPLER) {} // Uniform Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p) - : name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { } + : name(paramName), + size(typeSize), + uniformType(t), + precision(p), + format{ 0 }, + filterable(false), + multisample(false), + 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) { } + : name(paramName), + size(1), + precision(p), + subpassType(t), + format(f), + filterable(false), + multisample(false), + parameterType(SUBPASS) {} utils::CString name; size_t size; @@ -654,7 +736,10 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { SamplerType samplerType; SubpassType subpassType; SamplerFormat format; + bool filterable; bool multisample; + utils::CString transformName; + std::optional stages; enum { INVALID, UNIFORM, @@ -685,25 +770,33 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { struct Constant { utils::CString name; ConstantType type; - union { - int32_t i; - float f; - bool b; - } defaultValue; + ConstantValue defaultValue; + }; + + struct PushConstant { + utils::CString name; + ConstantType type; + ShaderStage stage; + }; + + struct CustomVariable { + utils::CString name; + Precision precision = Precision::DEFAULT; + bool hasPrecision = false; }; 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 VariableList = CustomVariable[MATERIAL_VARIABLES_COUNT]; using OutputList = std::vector; 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::MAX_SUPPORTED_RENDER_TARGET_COUNT, manually update post_process_inputs.fs" - " and post_process.fs"); + " and post_process_main.fs"); // Preview the first shader generated by the given CodeGenParams. // This is used to run Static Code Analysis before generating a package. @@ -713,16 +806,16 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { // Returns true if any of the parameter samplers matches the specified type. bool hasSamplerType(SamplerType samplerType) const noexcept; - static constexpr size_t MAX_PARAMETERS_COUNT = 48; static constexpr size_t MAX_SUBPASS_COUNT = 1; static constexpr size_t MAX_BUFFERS_COUNT = 4; - using ParameterList = Parameter[MAX_PARAMETERS_COUNT]; + using ParameterList = std::vector; using SubpassList = Parameter[MAX_SUBPASS_COUNT]; using BufferList = std::vector>; using ConstantList = std::vector; + using PushConstantList = std::vector; // returns the number of parameters declared in this material - uint8_t getParameterCount() const noexcept { return mParameterCount; } + size_t getParameterCount() const noexcept { return mParameters.size(); } // returns a list of at least getParameterCount() parameters const ParameterList& getParameters() const noexcept { return mParameters; } @@ -763,6 +856,10 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { void prepareToBuild(MaterialInfo& info) noexcept; + // Initialize internal push constants that will both be written to the shaders and material + // chunks (like user-defined spec constants). + void initPushConstants() 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. @@ -771,7 +868,7 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { // Multiple calls to findProperties accumulate the property sets across fragment // and vertex shaders in mProperties. bool findProperties(filament::backend::ShaderStage type, - MaterialBuilder::PropertyList& allProperties, + MaterialBuilder::PropertyList const& allProperties, CodeGenParams const& semanticCodeGenParams) noexcept; bool runSemanticAnalysis(MaterialInfo* inOutInfo, @@ -795,21 +892,16 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; } utils::CString mMaterialName; - utils::CString mFileName; + utils::CString mCompilationParameters; class ShaderCode { public: void setLineOffset(size_t offset) noexcept { mLineOffset = offset; } - void setUnresolved(const utils::CString& code) noexcept { - mIncludesResolved = false; + void setCode(const utils::CString& code) noexcept { 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); + const utils::CString& getCode() const noexcept { return mCode; } @@ -818,17 +910,16 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { private: utils::CString mCode; size_t mLineOffset = 0; - bool mIncludesResolved = false; }; ShaderCode mMaterialFragmentCode; ShaderCode mMaterialVertexCode; - - IncludeCallback mIncludeCallback = nullptr; + std::string_view mMaterialSource; PropertyList mProperties; ParameterList mParameters; ConstantList mConstants; + PushConstantList mPushConstants; SubpassList mSubpasses; VariableList mVariables; OutputList mOutputs; @@ -862,7 +953,6 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { bool mShadowMultiplier = false; bool mTransparentShadow = false; - uint8_t mParameterCount = 0; uint8_t mSubpassCount = 0; bool mDoubleSided = false; @@ -879,6 +969,8 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { bool mClearCoatIorChange = true; bool mFlipUV = true; + bool mLinearFog = false; + bool mShadowFarAttenuation = true; bool mMultiBounceAO = false; bool mMultiBounceAOSet = false; @@ -899,11 +991,24 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { filament::UserVariantFilterMask mVariantFilter = {}; bool mNoSamplerValidation = false; + + bool mUseDefaultDepthVariant = false; + + // Default api level is always 1. + uint32_t mApiLevel = 1; }; } // namespace filamat -template<> struct utils::EnableBitMaskOperators - : public std::true_type {}; +template<> +struct utils::EnableBitMaskOperators + : public std::true_type { +}; + +template<> +struct utils::EnableBitMaskOperators + : public std::true_type { +}; + #endif diff --git a/package/android/libs/filament/include/filamat/Package.h b/package/android/libs/filament/include/filamat/Package.h index 93e74a58..52967bfb 100644 --- a/package/android/libs/filament/include/filamat/Package.h +++ b/package/android/libs/filament/include/filamat/Package.h @@ -20,6 +20,7 @@ #include #include #include +#include // memcpy #include #include diff --git a/package/android/libs/filament/include/filament-generatePrefilterMipmap/generatePrefilterMipmap.h b/package/android/libs/filament/include/filament-generatePrefilterMipmap/generatePrefilterMipmap.h new file mode 100644 index 00000000..ab506c9f --- /dev/null +++ b/package/android/libs/filament/include/filament-generatePrefilterMipmap/generatePrefilterMipmap.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 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 + +#include +#include + +namespace filament { + +namespace backend { +class PixelBufferDescriptor; +} + +class Engine; +class Texture; + +struct UTILS_PUBLIC FaceOffsets { + using size_type = size_t; + union { + struct { + size_type px; //!< +x face offset in bytes + size_type nx; //!< -x face offset in bytes + size_type py; //!< +y face offset in bytes + size_type ny; //!< -y face offset in bytes + size_type pz; //!< +z face offset in bytes + size_type nz; //!< -z face offset in bytes + }; + size_type offsets[6]; + }; + size_type operator[](size_t const n) const noexcept { return offsets[n]; } + size_type& operator[](size_t const n) { return offsets[n]; } + FaceOffsets() noexcept = default; + explicit FaceOffsets(size_type const faceSize) noexcept { + px = faceSize * 0; + nx = faceSize * 1; + py = faceSize * 2; + ny = faceSize * 3; + pz = faceSize * 4; + nz = faceSize * 5; + } + FaceOffsets(const FaceOffsets& rhs) noexcept { + px = rhs.px; + nx = rhs.nx; + py = rhs.py; + ny = rhs.ny; + pz = rhs.pz; + nz = rhs.nz; + } + FaceOffsets& operator=(const FaceOffsets& rhs) noexcept { + px = rhs.px; + nx = rhs.nx; + py = rhs.py; + ny = rhs.ny; + pz = rhs.pz; + nz = rhs.nz; + return *this; + } +}; + +/** + * Options for environment prefiltering into reflection map + * + * @see generatePrefilterMipmap() + */ +struct UTILS_PUBLIC PrefilterOptions { + uint16_t sampleCount = 8; //!< sample count used for filtering + bool mirror = true; //!< whether the environment must be mirrored +private: + UTILS_UNUSED uintptr_t reserved[3] = {}; +}; + +UTILS_PUBLIC +void generatePrefilterMipmap(Texture* UTILS_NONNULL texture, Engine& engine, + backend::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, + PrefilterOptions const* UTILS_NULLABLE options = nullptr); + + +} // namespace filament diff --git a/package/android/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h b/package/android/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h index 903c4258..e894e148 100644 --- a/package/android/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h +++ b/package/android/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h @@ -227,7 +227,6 @@ class UTILS_PUBLIC IBLPrefilterContext { filament::Texture* outIrradianceTexture = nullptr); private: - filament::Texture* createIrradianceTexture(); IBLPrefilterContext& mContext; filament::Material* mKernelMaterial = nullptr; filament::Texture* mKernelTexture = nullptr; @@ -325,7 +324,6 @@ class UTILS_PUBLIC IBLPrefilterContext { // TODO: add a callback for when the processing is done? private: - filament::Texture* createReflectionsTexture(); IBLPrefilterContext& mContext; filament::Material* mKernelMaterial = nullptr; filament::Texture* mKernelTexture = nullptr; diff --git a/package/android/libs/filament/include/filament/Box.h b/package/android/libs/filament/include/filament/Box.h index da6638da..35042030 100644 --- a/package/android/libs/filament/include/filament/Box.h +++ b/package/android/libs/filament/include/filament/Box.h @@ -120,8 +120,11 @@ class UTILS_PUBLIC Box { } /** - * @deprecated Use transform() instead - * @see transform() + * Transform a Box by a linear transform and a translation. + * + * @param m a linear transform matrix + * @param box the box to transform + * @return the bounding box of the transformed box */ friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept { return transform(m.upperLeft(), m[3].xyz, box); @@ -183,7 +186,7 @@ struct UTILS_PUBLIC Aabb { * Returns the 8 corner vertices of the AABB. */ Corners getCorners() const { - return Aabb::Corners{ .vertices = { + return Corners{ .vertices = { { min.x, min.y, min.z }, { max.x, min.y, min.z }, { min.x, max.y, min.z }, @@ -235,8 +238,10 @@ struct UTILS_PUBLIC Aabb { } /** - * @deprecated Use transform() instead - * @see transform() + * Applies an affine transformation to the AABB. + * + * @param m the affine transformation to apply + * @return the bounding box of the transformed box */ Aabb transform(const math::mat4f& m) const noexcept { return transform(m.upperLeft(), m[3].xyz, *this); diff --git a/package/android/libs/filament/include/filament/BufferObject.h b/package/android/libs/filament/include/filament/BufferObject.h index 74a4b1ff..bf3ffe3d 100644 --- a/package/android/libs/filament/include/filament/BufferObject.h +++ b/package/android/libs/filament/include/filament/BufferObject.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -54,7 +55,7 @@ class UTILS_PUBLIC BufferObject : public FilamentAPI { using BufferDescriptor = backend::BufferDescriptor; using BindingType = backend::BufferObjectBinding; - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -73,11 +74,38 @@ class UTILS_PUBLIC BufferObject : public FilamentAPI { /** * The binding type for this buffer object. (defaults to VERTEX) - * @param BindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. + * @param bindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. * @return A reference to this Builder for chaining calls. */ Builder& bindingType(BindingType bindingType) noexcept; + /** + * Associate an optional name with this BufferObject for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this BufferObject + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this BufferObject for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this BufferObject + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the BufferObject and returns a pointer to it. After creation, the buffer * object is uninitialized. Use BufferObject::setBuffer() to initialize it. @@ -102,7 +130,7 @@ class UTILS_PUBLIC BufferObject : public FilamentAPI { * * @param engine Reference to the filament::Engine associated with this BufferObject. * @param buffer A BufferDescriptor representing the data used to initialize the BufferObject. - * @param byteOffset Offset in bytes into the BufferObject + * @param byteOffset Offset in bytes into the BufferObject. Must be multiple of 4. */ void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); diff --git a/package/android/libs/filament/include/filament/Camera.h b/package/android/libs/filament/include/filament/Camera.h index 74f34af0..abf8b2c1 100644 --- a/package/android/libs/filament/include/filament/Camera.h +++ b/package/android/libs/filament/include/filament/Camera.h @@ -61,7 +61,7 @@ namespace filament { * filament::Camera* myCamera = engine->createCamera(myCameraEntity); * myCamera->setProjection(45, 16.0/9.0, 0.1, 1.0); * myCamera->lookAt({0, 1.60, 1}, {0, 0, 0}); - * engine->destroyCameraComponent(myCamera); + * engine->destroyCameraComponent(myCameraEntity); * ~~~~~~~~~~~ * * @@ -243,10 +243,10 @@ class UTILS_PUBLIC Camera : public FilamentAPI { /** Utility to set the projection matrix from the field-of-view. * * @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180. - * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. - * @param direction direction of the \p fovInDegrees parameter. + * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param direction direction of the \p fovInDegrees parameter. * * @see Fov. */ @@ -256,9 +256,9 @@ class UTILS_PUBLIC Camera : public FilamentAPI { /** Utility to set the projection matrix from the focal length. * * @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0. - * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. */ void setLensProjection(double focalLengthInMillimeters, double aspect, double near, double far); @@ -270,8 +270,8 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * that is all 3 axis are mapped to [-1, 1]. * * @param projection custom projection matrix used for rendering and culling - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param near distance in world units from the camera to the near plane. + * @param far distance in world units from the camera to the far plane. \p far != \p near. */ void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept; @@ -282,8 +282,8 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * * @param projection custom projection matrix used for rendering * @param projectionForCulling custom projection matrix used for culling - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param near distance in world units from the camera to the near plane. + * @param far distance in world units from the camera to the far plane. \p far != \p near. */ void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling, double near, double far) noexcept; @@ -400,14 +400,14 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * engine.getTransformManager().getInstance(camera->getEntity()), model); * ~~~~~~~~~~~ * - * @param model The camera position and orientation provided as a rigid transform matrix. + * @param modelMatrix The camera position and orientation provided as a rigid transform matrix. * * @note The Camera "looks" towards its -z axis * * @warning \p model must be a rigid transform */ - void setModelMatrix(const math::mat4& model) noexcept; - void setModelMatrix(const math::mat4f& model) noexcept; //!< @overload + void setModelMatrix(const math::mat4& modelMatrix) noexcept; + void setModelMatrix(const math::mat4f& modelMatrix) noexcept; //!< @overload /** Set the position of an eye relative to this Camera (head). * diff --git a/package/android/libs/filament/include/filament/Color.h b/package/android/libs/filament/include/filament/Color.h index 30b77856..37939820 100644 --- a/package/android/libs/filament/include/filament/Color.h +++ b/package/android/libs/filament/include/filament/Color.h @@ -194,7 +194,7 @@ inline sRGBColorA Color::toSRGB(LinearColorA const& color) { } inline LinearColor Color::toLinear(RgbType type, math::float3 color) { - return (type == RgbType::LINEAR) ? color : Color::toLinear(color); + return (type == RgbType::LINEAR) ? color : toLinear(color); } // converts an RGBA color to linear space @@ -202,11 +202,11 @@ inline LinearColor Color::toLinear(RgbType type, math::float3 color) { inline LinearColorA Color::toLinear(RgbaType type, math::float4 color) { switch (type) { case RgbaType::sRGB: - return Color::toLinear(color) * math::float4{color.a, color.a, color.a, 1.0f}; + return toLinear(color) * math::float4{color.a, color.a, color.a, 1.0f}; case RgbaType::LINEAR: return color * math::float4{color.a, color.a, color.a, 1.0f}; case RgbaType::PREMULTIPLIED_sRGB: - return Color::toLinear(color); + return toLinear(color); case RgbaType::PREMULTIPLIED_LINEAR: return color; } diff --git a/package/android/libs/filament/include/filament/ColorGrading.h b/package/android/libs/filament/include/filament/ColorGrading.h index e5c8f3ca..3e4916fb 100644 --- a/package/android/libs/filament/include/filament/ColorGrading.h +++ b/package/android/libs/filament/include/filament/ColorGrading.h @@ -64,9 +64,8 @@ class ColorSpace; * Performance * =========== * - * Creating a new ColorGrading object may be more expensive than other Filament objects as a - * 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on - * the CPU. + * Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may + * need to be generated. The generation of this LUT, if necessary, may happen on the CPU. * * Ordering * ======== @@ -155,6 +154,9 @@ class UTILS_PUBLIC ColorGrading : public FilamentAPI { * 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium * quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit * LUT, and a ultra quality will use a 64x64x64 16 bit LUT. + * + * This setting has no effect if generating a 1D LUT. + * * This overrides the values set by format() and dimensions(). * * The default quality is medium. @@ -169,6 +171,8 @@ class UTILS_PUBLIC ColorGrading : public FilamentAPI { * When color grading is implemented using a 3D LUT, this sets the texture format of * of the LUT. This overrides the value set by quality(). * + * This setting has no effect if generating a 1D LUT. + * * The default is INTEGER * * @param format The desired format of the 3D LUT. @@ -181,6 +185,8 @@ class UTILS_PUBLIC ColorGrading : public FilamentAPI { * When color grading is implemented using a 3D LUT, this sets the dimension of the LUT. * This overrides the value set by quality(). * + * This setting has no effect if generating a 1D LUT. + * * The default is 32 * * @param dim The desired dimension of the LUT. Between 16 and 64. diff --git a/package/android/libs/filament/include/filament/Engine.h b/package/android/libs/filament/include/filament/Engine.h index b741d3ab..c9dee81c 100644 --- a/package/android/libs/filament/include/filament/Engine.h +++ b/package/android/libs/filament/include/filament/Engine.h @@ -17,6 +17,7 @@ #ifndef TNT_FILAMENT_ENGINE_H #define TNT_FILAMENT_ENGINE_H + #include #include @@ -24,10 +25,16 @@ #include #include +#include + +#include +#include +#include #include #include + namespace utils { class Entity; class EntityManager; @@ -36,6 +43,10 @@ class JobSystem; namespace filament { +namespace backend { +class Driver; +} // backend + class BufferObject; class Camera; class ColorGrading; @@ -53,6 +64,7 @@ class Scene; class Skybox; class Stream; class SwapChain; +class Sync; class Texture; class VertexBuffer; class View; @@ -179,6 +191,11 @@ class UTILS_PUBLIC Engine { using DriverConfig = backend::Platform::DriverConfig; using FeatureLevel = backend::FeatureLevel; using StereoscopicType = backend::StereoscopicType; + using Driver = backend::Driver; + using GpuContextPriority = backend::Platform::GpuContextPriority; + using AsynchronousMode = backend::AsynchronousMode; + using AsyncCompletionCallback = std::function; + using AsyncCallId = backend::AsyncCallId; /** * Config is used to define the memory footprint used by the engine, such as the @@ -288,19 +305,37 @@ class UTILS_PUBLIC Engine { */ uint32_t jobSystemThreadCount = 0; - /* - * Number of most-recently destroyed textures to track for use-after-free. + /** + * When uploading vertex or index data, the Filament Metal backend copies data + * into a shared staging area before transferring it to the GPU. This setting controls + * the total size of the buffer used to perform these allocations. + * + * Higher values can improve performance when performing many uploads across a small + * number of frames. + * + * This buffer remains alive throughout the lifetime of the Engine, so this size adds to the + * memory footprint of the app and should be set as conservative as possible. * - * This will cause the backend to throw an exception when a texture is freed but still bound - * to a SamplerGroup and used in a draw call. 0 disables completely. + * A value of 0 disables the shared staging buffer entirely; uploads will acquire an + * individual buffer from a pool of shared buffers. * - * Currently only respected by the Metal backend. + * Only respected by the Metal backend. */ - size_t textureUseAfterFreePoolSize = 0; + size_t metalUploadBufferSizeBytes = 512 * 1024; + + /** + * The action to take if a Drawable cannot be acquired. + * + * Each frame rendered requires a CAMetalDrawable texture, which is + * presented on-screen at the completion of each frame. These are + * limited and provided round-robin style by the system. + */ + bool metalDisablePanicOnDrawableFailure = false; /** * Set to `true` to forcibly disable parallel shader compilation in the backend. * Currently only honored by the GL and Metal backends. + * @deprecated use "backend.disable_parallel_shader_compile" feature flag instead */ bool disableParallelShaderCompile = false; @@ -315,7 +350,7 @@ class UTILS_PUBLIC Engine { * * @see View::setStereoscopicOptions */ - StereoscopicType stereoscopicType = StereoscopicType::INSTANCED; + StereoscopicType stereoscopicType = StereoscopicType::NONE; /* * The number of eyes to render when stereoscopic rendering is enabled. Supported values are @@ -332,17 +367,103 @@ class UTILS_PUBLIC Engine { uint32_t resourceAllocatorCacheSizeMB = 64; /* - * This value determines for how many frames are texture entries kept in the cache. + * This value determines how many frames texture entries are kept for in the cache. This + * is a soft limit, meaning some texture older than this are allowed to stay in the cache. + * Typically only one texture is evicted per frame. + * The default is 1. */ - uint32_t resourceAllocatorCacheMaxAge = 2; + uint32_t resourceAllocatorCacheMaxAge = 1; /* * Disable backend handles use-after-free checks. + * @deprecated use "backend.disable_handle_use_after_free_check" feature flag instead */ bool disableHandleUseAfterFreeCheck = false; + + /* + * Sets a preferred shader language for Filament to use. + * + * The Metal backend supports two shader languages: MSL (Metal Shading Language) and + * METAL_LIBRARY (precompiled .metallib). This option controls which shader language is + * used when materials contain both. + * + * By default, when preferredShaderLanguage is unset, Filament will prefer METAL_LIBRARY + * shaders if present within a material, falling back to MSL. Setting + * preferredShaderLanguage to ShaderLanguage::MSL will instead instruct Filament to check + * for the presence of MSL in a material first, falling back to METAL_LIBRARY if MSL is not + * present. + * + * When using a non-Metal backend, setting this has no effect. + */ + enum class ShaderLanguage { + DEFAULT = 0, + MSL = 1, + METAL_LIBRARY = 2, + }; + ShaderLanguage preferredShaderLanguage = ShaderLanguage::DEFAULT; + + /* + * When the OpenGL ES backend is used, setting this value to true will force a GLES2.0 + * context if supported by the Platform, or if not, will have the backend pretend + * it's a GLES2 context. Ignored on other backends. + */ + bool forceGLES2Context = false; + + /** + * Assert the native window associated to a SwapChain is valid when calling makeCurrent(). + * This is only supported for: + * - PlatformEGLAndroid + * @deprecated use "backend.opengl.assert_native_window_is_valid" feature flag instead + */ + bool assertNativeWindowIsValid = false; + + /** + * GPU context priority level. Controls GPU work scheduling and preemption. + */ + GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT; + + /** + * The initial size in bytes of the shared uniform buffer used for material instance + * batching. + * + * If the buffer runs out of space during a frame, it will be automatically reallocated + * with a larger capacity. Setting an appropriate initial size can help avoid runtime + * reallocations, which can cause a minor performance stutter, at the cost of higher + * initial memory usage. + */ + uint32_t sharedUboInitialSizeInBytes = 256 * 64; + + /** + * Asynchronous mode for the engine. Defines how asynchronous operations are handled. + * Note that selecting a non-NONE mode does not guarantee asynchronous methods are + * supported, as the underlying backend or the feature flag may override this configuration. + * Always validate availability via Engine::isAsynchronousModeEnabled() before + * invoking asynchronous methods. + */ + AsynchronousMode asynchronousMode = AsynchronousMode::NONE; }; + /** + * Feature flags can be enabled or disabled when the Engine is built. Some Feature flags can + * also be toggled at any time. Feature flags should alawys use their default value unless + * the feature enabled by the flag is faulty. Feature flags provide a last resort way to + * disable problematic features. + * Feature flags are intended to have a short life-time and are regularly removed as features + * mature. + */ + struct FeatureFlag { + char const* UTILS_NONNULL name; //!< name of the feature flag + char const* UTILS_NONNULL description; //!< short description + bool const* UTILS_NONNULL value; //!< pointer to the value of the flag + bool constant = true; //!< whether the flag is constant after construction + }; + + /** + * Returns the list of available feature flags + */ + utils::Slice getFeatureFlags() const noexcept; + #if UTILS_HAS_THREADING using CreateCallback = void(void* UTILS_NULLABLE user, void* UTILS_NONNULL token); #endif @@ -415,6 +536,21 @@ class UTILS_PUBLIC Engine { */ Builder& paused(bool paused) noexcept; + /** + * Set a feature flag value. This is the only way to set constant feature flags. + * @param name feature name + * @param value true to enable, false to disable + * @return A reference to this Builder for chaining calls. + */ + Builder& feature(char const* UTILS_NONNULL name, bool value) noexcept; + + /** + * Enables a list of features. + * @param list list of feature names to enable. + * @return A reference to this Builder for chaining calls. + */ + Builder& features(std::initializer_list list) noexcept; + #if UTILS_HAS_THREADING /** * Creates the filament Engine asynchronously. @@ -448,7 +584,7 @@ class UTILS_PUBLIC Engine { Platform* UTILS_NULLABLE platform = nullptr, void* UTILS_NULLABLE sharedContext = nullptr, const Config* UTILS_NULLABLE config = nullptr) { - return Engine::Builder() + return Builder() .backend(backend) .platform(platform) .sharedContext(sharedContext) @@ -468,7 +604,7 @@ class UTILS_PUBLIC Engine { Platform* UTILS_NULLABLE platform = nullptr, void* UTILS_NULLABLE sharedContext = nullptr, const Config* UTILS_NULLABLE config = nullptr) { - Engine::Builder() + Builder() .backend(backend) .platform(platform) .sharedContext(sharedContext) @@ -493,6 +629,11 @@ class UTILS_PUBLIC Engine { static Engine* UTILS_NULLABLE getEngine(void* UTILS_NONNULL token); #endif + /** + * @return the Driver instance used by this Engine. + * @see OpenGLPlatform + */ + backend::Driver const* UTILS_NONNULL getDriver() const noexcept; /** * Destroy the Engine instance and all associated resources. @@ -614,6 +755,14 @@ class UTILS_PUBLIC Engine { */ bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept; + /** + * Checks if the engine is set up for asynchronous operation. If it returns true, the + * asynchronous versions of the APIs are available for use. + * + * @return true if the engine supports asynchronous operation. + */ + bool isAsynchronousModeEnabled() const noexcept; + /** * Retrieves the configuration settings of this Engine. * @@ -770,9 +919,19 @@ class UTILS_PUBLIC Engine { */ Fence* UTILS_NONNULL createFence() noexcept; + /** + * Creates a Sync. + * @param callback A callback that will be invoked when the handle for + * the created sync is set + * + * @return A pointer to the newly created Sync. + */ + Sync* UTILS_NONNULL createSync() noexcept; + bool destroy(const BufferObject* UTILS_NULLABLE p); //!< Destroys a BufferObject object. bool destroy(const VertexBuffer* UTILS_NULLABLE p); //!< Destroys an VertexBuffer object. bool destroy(const Fence* UTILS_NULLABLE p); //!< Destroys a Fence object. + bool destroy(const Sync* UTILS_NULLABLE p); //!< Destroys a Sync object. bool destroy(const IndexBuffer* UTILS_NULLABLE p); //!< Destroys an IndexBuffer object. bool destroy(const SkinningBuffer* UTILS_NULLABLE p); //!< Destroys a SkinningBuffer object. bool destroy(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Destroys a MorphTargetBuffer object. @@ -800,24 +959,117 @@ class UTILS_PUBLIC Engine { bool destroy(const InstanceBuffer* UTILS_NULLABLE p); //!< Destroys an InstanceBuffer object. void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity - bool isValid(const BufferObject* UTILS_NULLABLE p); //!< Tells whether a BufferObject object is valid - bool isValid(const VertexBuffer* UTILS_NULLABLE p); //!< Tells whether an VertexBuffer object is valid - bool isValid(const Fence* UTILS_NULLABLE p); //!< Tells whether a Fence object is valid - bool isValid(const IndexBuffer* UTILS_NULLABLE p); //!< Tells whether an IndexBuffer object is valid - bool isValid(const SkinningBuffer* UTILS_NULLABLE p); //!< Tells whether a SkinningBuffer object is valid - bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Tells whether a MorphTargetBuffer object is valid - bool isValid(const IndirectLight* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid - bool isValid(const Material* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid - bool isValid(const Renderer* UTILS_NULLABLE p); //!< Tells whether a Renderer object is valid - bool isValid(const Scene* UTILS_NULLABLE p); //!< Tells whether a Scene object is valid - bool isValid(const Skybox* UTILS_NULLABLE p); //!< Tells whether a SkyBox object is valid - bool isValid(const ColorGrading* UTILS_NULLABLE p); //!< Tells whether a ColorGrading object is valid - bool isValid(const SwapChain* UTILS_NULLABLE p); //!< Tells whether a SwapChain object is valid - bool isValid(const Stream* UTILS_NULLABLE p); //!< Tells whether a Stream object is valid - bool isValid(const Texture* UTILS_NULLABLE p); //!< Tells whether a Texture object is valid - bool isValid(const RenderTarget* UTILS_NULLABLE p); //!< Tells whether a RenderTarget object is valid - bool isValid(const View* UTILS_NULLABLE p); //!< Tells whether a View object is valid - bool isValid(const InstanceBuffer* UTILS_NULLABLE p); //!< Tells whether an InstanceBuffer object is valid + /** Tells whether a BufferObject object is valid */ + bool isValid(const BufferObject* UTILS_NULLABLE p) const; + /** Tells whether an VertexBuffer object is valid */ + bool isValid(const VertexBuffer* UTILS_NULLABLE p) const; + /** Tells whether a Fence object is valid */ + bool isValid(const Fence* UTILS_NULLABLE p) const; + /** Tells whether a Sync object is valid */ + bool isValid(const Sync* UTILS_NULLABLE p) const; + /** Tells whether an IndexBuffer object is valid */ + bool isValid(const IndexBuffer* UTILS_NULLABLE p) const; + /** Tells whether a SkinningBuffer object is valid */ + bool isValid(const SkinningBuffer* UTILS_NULLABLE p) const; + /** Tells whether a MorphTargetBuffer object is valid */ + bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p) const; + /** Tells whether an IndirectLight object is valid */ + bool isValid(const IndirectLight* UTILS_NULLABLE p) const; + /** Tells whether an Material object is valid */ + bool isValid(const Material* UTILS_NULLABLE p) const; + /** Tells whether an MaterialInstance object is valid. Use this if you already know + * which Material this MaterialInstance belongs to. DO NOT USE getMaterial(), this would + * defeat the purpose of validating the MaterialInstance. + */ + bool isValid(const Material* UTILS_NONNULL m, const MaterialInstance* UTILS_NULLABLE p) const; + /** Tells whether an MaterialInstance object is valid. Use this if the Material the + * MaterialInstance belongs to is not known. This method can be expensive. + */ + bool isValidExpensive(const MaterialInstance* UTILS_NULLABLE p) const; + /** Tells whether a Renderer object is valid */ + bool isValid(const Renderer* UTILS_NULLABLE p) const; + /** Tells whether a Scene object is valid */ + bool isValid(const Scene* UTILS_NULLABLE p) const; + /** Tells whether a SkyBox object is valid */ + bool isValid(const Skybox* UTILS_NULLABLE p) const; + /** Tells whether a ColorGrading object is valid */ + bool isValid(const ColorGrading* UTILS_NULLABLE p) const; + /** Tells whether a SwapChain object is valid */ + bool isValid(const SwapChain* UTILS_NULLABLE p) const; + /** Tells whether a Stream object is valid */ + bool isValid(const Stream* UTILS_NULLABLE p) const; + /** Tells whether a Texture object is valid */ + bool isValid(const Texture* UTILS_NULLABLE p) const; + /** Tells whether a RenderTarget object is valid */ + bool isValid(const RenderTarget* UTILS_NULLABLE p) const; + /** Tells whether a View object is valid */ + bool isValid(const View* UTILS_NULLABLE p) const; + /** Tells whether an InstanceBuffer object is valid */ + bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const; + + /** + * Retrieve the count of each resource tracked by Engine. + * This is intended for debugging. + * @{ + */ + size_t getBufferObjectCount() const noexcept; + size_t getViewCount() const noexcept; + size_t getSceneCount() const noexcept; + size_t getSwapChainCount() const noexcept; + size_t getStreamCount() const noexcept; + size_t getIndexBufferCount() const noexcept; + size_t getSkinningBufferCount() const noexcept; + size_t getMorphTargetBufferCount() const noexcept; + size_t getInstanceBufferCount() const noexcept; + size_t getVertexBufferCount() const noexcept; + size_t getIndirectLightCount() const noexcept; + size_t getMaterialCount() const noexcept; + size_t getTextureCount() const noexcept; + size_t getSkyboxeCount() const noexcept; + size_t getColorGradingCount() const noexcept; + size_t getRenderTargetCount() const noexcept; + /** @} */ + + /** + * This asynchronously executes user-defined commands. The commands are queued sequentially + * alongside other asynchronous operations (see Texture, VertexBuffer, and IndexBuffer) and + * guaranteed to be executed in the exact order they were invoked. + * + * Beware of overusing this method. It shares the execution queue with other asynchronous tasks + * like texture updates, so flooding it can delay those critical engine tasks. The recommended + * practice is to use this method for resource preparation, such as asset loading(images/meshes). + * This facilitates an efficient chaining pattern, where subsequent asynchronous operations + * (e.g., creating textures/vertex buffers) can be initiated directly within the completion + * callback. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param command The custom command to be executed. + * @param handler The handler from which `onComplete` is invoked. If null, it's called from the + * main thread. + * @param onComplete The callback function that runs once the command has finished. + * @param user The custom data that will be passed as an argument to the `onComplete`. + * @return A unique identifier for the asynchronous call. + */ + AsyncCallId runCommandAsync(utils::Invocable&& command, + backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback onComplete, + void* UTILS_NULLABLE user = nullptr); + + /** + * Cancel the pending asynchronous call pointed to by `id`, which is retrieved whenever you + * invoke a non-blocking version of method on an object, such as `Texture::setImageAsync` or + * `BufferObject::setBufferAsync`. + * + * @param id The unique identifier for the asynchronous call to be canceled. + * @return Returns true upon successful cancellation. It returns false if the asynchronous + * operation cannot be canceled because it is currently running, has finished, or has previously + * been canceled. + */ + bool cancelAsyncCall(AsyncCallId id); /** * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until @@ -831,6 +1083,25 @@ class UTILS_PUBLIC Engine { */ void flushAndWait(); + /** + * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until + * all commands to this point are executed. Note that does guarantee that the + * hardware is actually finished. + * + * A timeout can be specified, if for some reason this flushAndWait doesn't complete before the timeout, it will + * return false, true otherwise. + * + *

This is typically used right after destroying the SwapChain, + * in cases where a guarantee about the SwapChain destruction is needed in a + * timely fashion, such as when responding to Android's + * android.view.SurfaceHolder.Callback.surfaceDestroyed

+ * + * @param timeout A timeout in nanoseconds + * @return true if successful, false if flushAndWait timed out, in which case it wasn't successful and commands + * might still be executing on both the CPU and GPU sides. + */ + bool flushAndWait(uint64_t timeout); + /** * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) but does not wait * for commands to be either executed or the hardware finished. @@ -840,6 +1111,15 @@ class UTILS_PUBLIC Engine { */ void flush(); + /** + * Get paused state of rendering thread. + * + *

Warning: This is an experimental API. + * + * @see setPaused + */ + bool isPaused() const noexcept(UTILS_HAS_THREADING); + /** * Pause or resume rendering thread. * @@ -865,6 +1145,14 @@ class UTILS_PUBLIC Engine { */ void pumpMessageQueues(); + /** + * Switch the command queue to unprotected mode. Protected mode can be activated via + * Renderer::beginFrame() using a protected SwapChain. + * @see Renderer + * @see SwapChain + */ + void unprotected() noexcept; + /** * Returns the default Material. * @@ -948,8 +1236,54 @@ class UTILS_PUBLIC Engine { void resetBackendState() noexcept; #endif + /** + * Get the current time. This is a convenience function that simply returns the + * time in nanosecond since epoch of std::chrono::steady_clock. + * A possible implementation is: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * return std::chrono::steady_clock::now().time_since_epoch().count(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @return current time in nanosecond since epoch of std::chrono::steady_clock. + * @see Renderer::beginFrame() + */ + static uint64_t getSteadyClockTimeNano() noexcept; + + DebugRegistry& getDebugRegistry() noexcept; + /** + * Check if a feature flag exists + * @param name name of the feature flag to check + * @return true if the feature flag exists, false otherwise + */ + inline bool hasFeatureFlag(char const* UTILS_NONNULL name) noexcept { + return getFeatureFlag(name).has_value(); + } + + /** + * Set the value of a non-constant feature flag. + * @param name name of the feature flag to set + * @param value value to set + * @return true if the value was set, false if the feature flag is constant or doesn't exist. + */ + bool setFeatureFlag(char const* UTILS_NONNULL name, bool value) noexcept; + + /** + * Retrieves the value of any feature flag. + * @param name name of the feature flag + * @return the value of the flag if it exists + */ + std::optional getFeatureFlag(char const* UTILS_NONNULL name) const noexcept; + + /** + * Returns a pointer to a non-constant feature flag value. + * @param name name of the feature flag + * @return a pointer to the feature flag value, or nullptr if the feature flag is constant or doesn't exist + */ + bool* UTILS_NULLABLE getFeatureFlagPtr(char const* UTILS_NONNULL name) const noexcept; + protected: //! \privatesection Engine() noexcept = default; diff --git a/package/android/libs/filament/include/filament/FilamentAPI.h b/package/android/libs/filament/include/filament/FilamentAPI.h index 19d6ba24..d59e051d 100644 --- a/package/android/libs/filament/include/filament/FilamentAPI.h +++ b/package/android/libs/filament/include/filament/FilamentAPI.h @@ -19,6 +19,8 @@ #include #include +#include +#include #include @@ -54,6 +56,37 @@ class UTILS_PUBLIC FilamentAPI { template using BuilderBase = utils::PrivateImplementation; +// This needs to be public because it is used in the following template. +UTILS_PUBLIC void builderMakeName(utils::ImmutableCString& outName, const char* name, size_t len) noexcept; + +template +class UTILS_PUBLIC BuilderNameMixin { +public: + UTILS_DEPRECATED + Builder& name(const char* name, size_t len) noexcept { + builderMakeName(mName, name, len); + return static_cast(*this); + } + + Builder& name(utils::StaticString const& name) noexcept { + builderMakeName(mName, name.data(), name.size()); + return static_cast(*this); + } + + utils::ImmutableCString const& getName() const noexcept { return mName; } + + utils::ImmutableCString const& getNameOrDefault() const noexcept { + if (const auto& name = getName(); !name.empty()) { + return name; + } + static const utils::ImmutableCString sDefaultName = "(none)"; + return sDefaultName; + } + +private: + utils::ImmutableCString mName; +}; + } // namespace filament #endif // TNT_FILAMENT_FILAMENTAPI_H diff --git a/package/android/libs/filament/include/filament/IndexBuffer.h b/package/android/libs/filament/include/filament/IndexBuffer.h index 35b8a10e..9ac7dd3f 100644 --- a/package/android/libs/filament/include/filament/IndexBuffer.h +++ b/package/android/libs/filament/include/filament/IndexBuffer.h @@ -26,7 +26,9 @@ #include #include +#include +#include #include #include @@ -50,6 +52,9 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { public: using BufferDescriptor = backend::BufferDescriptor; + using AsyncCompletionCallback = + std::function; + using AsyncCallId = backend::AsyncCallId; /** * Type of the index buffer @@ -59,7 +64,7 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices }; - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -83,6 +88,56 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { */ Builder& bufferType(IndexType indexType) noexcept; + /** + * Associate an optional name with this IndexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this IndexBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this IndexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this IndexBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + + /** + * Specifies a callback that will execute once the resource's data has been fully allocated + * within the GPU memory. This enables the resource creation process to be handled + * asynchronously. + * + * Any asynchronous calls made during a resource's asynchronous creation (using this method) + * are safe because they are queued and executed in sequence. However, invoking regular + * methods on the same resource before it's fully ready is unsafe and may cause undefined + * behavior. Users can call the `isCreationComplete()` method for the resource to confirm + * when the resource is ready for regular API calls. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * @return This Builder, for chaining calls. + */ + Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback = nullptr, + void* UTILS_NULLABLE user = nullptr) noexcept; + /** * Creates the IndexBuffer object and returns a pointer to it. After creation, the index * buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer. @@ -103,22 +158,59 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { }; /** - * Asynchronously copy-initializes a region of this IndexBuffer from the data provided. + * Copy-initializes a region of this IndexBuffer from the data provided. * * @param engine Reference to the filament::Engine to associate this IndexBuffer with. * @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer. * BufferDescriptor points to raw, untyped data that will be interpreted as * either 16-bit or 32-bits indices based on the Type of this IndexBuffer. - * @param byteOffset Offset in *bytes* into the IndexBuffer + * @param byteOffset Offset in *bytes* into the IndexBuffer. Must be multiple of 4. */ void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + /** + * An asynchronous version of `setBuffer()`. + * Copy-initializes a region of this IndexBuffer from the data provided. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param engine Reference to the filament::Engine to associate this IndexBuffer with. + * @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer. + * BufferDescriptor points to raw, untyped data that will be interpreted as + * either 16-bit or 32-bits indices based on the Type of this IndexBuffer. + * @param byteOffset Offset in *bytes* into the IndexBuffer. Must be multiple of 4. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + */ + AsyncCallId setBufferAsync(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset, + backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr); + /** * Returns the size of this IndexBuffer in elements. * @return The number of indices the IndexBuffer holds. */ size_t getIndexCount() const noexcept; + /** + * This non-blocking method checks if the resource has finished creation. If the resource + * creation was initiated asynchronously, it will return true only after all related + * asynchronous tasks are complete. If the resource was created normally without using async + * method, it will always return true. + * + * @return Whether the resource is created. + * + * @see Builder::async() + */ + bool isCreationComplete() const noexcept; + protected: // prevent heap allocation ~IndexBuffer() = default; diff --git a/package/android/libs/filament/include/filament/IndirectLight.h b/package/android/libs/filament/include/filament/IndirectLight.h index c230dac8..f5c2cfbe 100644 --- a/package/android/libs/filament/include/filament/IndirectLight.h +++ b/package/android/libs/filament/include/filament/IndirectLight.h @@ -158,6 +158,8 @@ class UTILS_PUBLIC IndirectLight : public FilamentAPI { * * @return This Builder, for chaining calls. * + * @see Material::Builder::sphericalHarmonicsBandCount() + * * @note * Because the coefficients are pre-scaled, `sh[0]` is the environment's * average irradiance. @@ -337,13 +339,24 @@ class UTILS_PUBLIC IndirectLight : public FilamentAPI { static math::float4 getColorEstimate(const math::float3 sh[UTILS_NONNULL 9], math::float3 direction) noexcept; - - /** @deprecated use static versions instead */ - UTILS_DEPRECATED + /** + * Helper to estimate the direction of the dominant light in the environment represented by + * spherical harmonics. + * Spherical harmonics must be set in the Builder or the result is undefined. + * @see getDirectionEstimate(const math::float3) + * @see Builder::irradiance(uint8_t, math::float3 const*) + * @see Builder::radiance(uint8_t, math::float3 const*) + */ math::float3 getDirectionEstimate() const noexcept; - /** @deprecated use static versions instead */ - UTILS_DEPRECATED + /** + * Helper to estimate the color and relative intensity of the environment represented by + * spherical harmonics in a given direction. + * Spherical harmonics must be set in the Builder or the result is undefined. + * @see getColorEstimate(const math::float3, math::float3) + * @see Builder::irradiance(uint8_t, math::float3 const*) + * @see Builder::radiance(uint8_t, math::float3 const*) + */ math::float4 getColorEstimate(math::float3 direction) const noexcept; protected: diff --git a/package/android/libs/filament/include/filament/InstanceBuffer.h b/package/android/libs/filament/include/filament/InstanceBuffer.h index 2135152d..8e7f788c 100644 --- a/package/android/libs/filament/include/filament/InstanceBuffer.h +++ b/package/android/libs/filament/include/filament/InstanceBuffer.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -38,13 +39,13 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI { struct BuilderDetails; public: - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: /** - * @param instanceCount the number of instances this InstanceBuffer will support, must be + * @param instanceCount The number of instances this InstanceBuffer will support, must be * >= 1 and <= \c Engine::getMaxAutomaticInstances() * @see Engine::getMaxAutomaticInstances */ @@ -70,10 +71,37 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI { */ Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept; + /** + * Associate an optional name with this InstanceBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this InstanceBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this InstanceBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this InstanceBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the InstanceBuffer object and returns a pointer to it. */ - InstanceBuffer* UTILS_NONNULL build(Engine& engine); + InstanceBuffer* UTILS_NONNULL build(Engine& engine) const; private: friend class FInstanceBuffer; @@ -96,6 +124,13 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI { void setLocalTransforms(math::mat4f const* UTILS_NONNULL localTransforms, size_t count, size_t offset = 0); + /** + * Returns the local transform for a given instance. + * @param index The index of the instance. + * @return The local transform of the instance. + */ + math::mat4f const& getLocalTransform(size_t index); + protected: // prevent heap allocation ~InstanceBuffer() = default; diff --git a/package/android/libs/filament/include/filament/Material.h b/package/android/libs/filament/include/filament/Material.h index b4b9bbed..3625bf6e 100644 --- a/package/android/libs/filament/include/filament/Material.h +++ b/package/android/libs/filament/include/filament/Material.h @@ -69,6 +69,20 @@ class UTILS_PUBLIC Material : public FilamentAPI { using ShaderModel = backend::ShaderModel; using SubpassType = backend::SubpassType; + /** + * Defines whether a material instance should use UBO batching or not. + */ + enum class UboBatchingMode { + /** + * For default, it follows the engine settings. + * If UBO batching is enabled on the engine and the material domain is SURFACE, it + * turns on the UBO batching. Otherwise, it turns off the UBO batching. + */ + DEFAULT, + //! Disable the Ubo Batching for this material + DISABLED, + }; + /** * Holds information about a material parameter. */ @@ -103,6 +117,11 @@ class UTILS_PUBLIC Material : public FilamentAPI { Builder& operator=(Builder const& rhs) noexcept; Builder& operator=(Builder&& rhs) noexcept; + enum class ShadowSamplingQuality : uint8_t { + HARD, // 2x2 PCF + LOW // 3x3 gaussian filter + }; + /** * Specifies the material data. The material data is a binary blob produced by * libfilamat or by matc. @@ -113,10 +132,10 @@ class UTILS_PUBLIC Material : public FilamentAPI { Builder& package(const void* UTILS_NONNULL payload, size_t size); template - using is_supported_constant_parameter_t = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value>::type; + using is_supported_constant_parameter_t = std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v>; /** * Specialize a constant parameter specified in the material definition with a concrete @@ -140,6 +159,33 @@ class UTILS_PUBLIC Material : public FilamentAPI { return constant(name, strlen(name), value); } + /** + * Sets the quality of the indirect lights computations. This is only taken into account + * if this material is lit and in the surface domain. This setting will affect the + * IndirectLight computation if one is specified on the Scene and Spherical Harmonics + * are used for the irradiance. + * + * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default). + * @return Reference to this Builder for chaining calls. + * @see IndirectLight + */ + Builder& sphericalHarmonicsBandCount(size_t shBandCount) noexcept; + + /** + * Set the quality of shadow sampling. This is only taken into account + * if this material is lit and in the surface domain. + * @param quality + * @return + */ + Builder& shadowSamplingQuality(ShadowSamplingQuality quality) noexcept; + + /** + * Set the batching mode of the instances created from this material. + * @param uboBatchingMode + * @return + */ + Builder& uboBatching(UboBatchingMode uboBatchingMode) noexcept; + /** * Creates the Material object and returns a pointer to it. * @@ -152,7 +198,7 @@ class UTILS_PUBLIC Material : public FilamentAPI { * memory or other resources. * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. */ - Material* UTILS_NULLABLE build(Engine& engine); + Material* UTILS_NULLABLE build(Engine& engine) const; private: friend class FMaterial; }; @@ -325,6 +371,24 @@ class UTILS_PUBLIC Material : public FilamentAPI { //! Indicates whether an existing parameter is a sampler or not. bool isSampler(const char* UTILS_NONNULL name) const noexcept; + /** + * Returns a view of the material source (.mat which is a JSON-ish file) string, + * if it has been set. Otherwise, it returns a view of an empty string. + * The lifetime of the string_view is tied to the lifetime of the Material. + */ + std::string_view getSource() const noexcept; + /** + * + * Gets the name of the transform field associated for the given sampler parameter. + * In the case where the parameter does not have a transform name field, it will return nullptr. + * + * @param samplerName the name of the sampler parameter to query. + * + * @return If exists, the transform name value otherwise returns a nullptr. + */ + const char* UTILS_NULLABLE getParameterTransformName( + const char* UTILS_NONNULL samplerName) const noexcept; + /** * Sets the value of the given parameter on this material's default instance. * diff --git a/package/android/libs/filament/include/filament/MaterialChunkType.h b/package/android/libs/filament/include/filament/MaterialChunkType.h index c80ac7d8..e321b386 100644 --- a/package/android/libs/filament/include/filament/MaterialChunkType.h +++ b/package/android/libs/filament/include/filament/MaterialChunkType.h @@ -45,17 +45,20 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialEssl1 = charTo64bitNum("MAT_ESS1"), MaterialSpirv = charTo64bitNum("MAT_SPIR"), MaterialMetal = charTo64bitNum("MAT_METL"), + MaterialWgsl = charTo64bitNum("MAT_WGSL"), MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"), MaterialShaderModels = charTo64bitNum("MAT_SMDL"), - MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), - MaterialUniformBindings = charTo64bitNum("MAT_UNIF"), MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"), MaterialAttributeInfo = charTo64bitNum("MAT_ATTR"), + MaterialDescriptorBindingsInfo = charTo64bitNum("MAT_DBDI"), + MaterialDescriptorSetLayoutInfo = charTo64bitNum("MAT_DSLI"), MaterialProperties = charTo64bitNum("MAT_PROP"), MaterialConstants = charTo64bitNum("MAT_CONS"), + MaterialPushConstants = charTo64bitNum("MAT_PCON"), MaterialName = charTo64bitNum("MAT_NAME"), MaterialVersion = charTo64bitNum("MAT_VERS"), + MaterialCompilationParameters = charTo64bitNum("MAT_CPRM"), MaterialCacheId = charTo64bitNum("MAT_UUID"), MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"), MaterialShading = charTo64bitNum("MAT_SHAD"), @@ -91,10 +94,15 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialVertexDomain = charTo64bitNum("MAT_VEDO"), MaterialInterpolation = charTo64bitNum("MAT_INTR"), + MaterialStereoscopicType = charTo64bitNum("MAT_STER"), DictionaryText = charTo64bitNum("DIC_TEXT"), DictionarySpirv = charTo64bitNum("DIC_SPIR"), DictionaryMetalLibrary = charTo64bitNum("DIC_MLIB"), + + MaterialCrc32 = charTo64bitNum("MAT_CRC "), + + MaterialSource = charTo64bitNum("MAT_SRC "), }; } // namespace filamat diff --git a/package/android/libs/filament/include/filament/MaterialEnums.h b/package/android/libs/filament/include/filament/MaterialEnums.h index 9f348481..fb2bea79 100644 --- a/package/android/libs/filament/include/filament/MaterialEnums.h +++ b/package/android/libs/filament/include/filament/MaterialEnums.h @@ -28,7 +28,16 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 51; +static constexpr size_t MATERIAL_VERSION = 68; + +// Those are the api levels that are used in the source material file (.mat) +// +// The level used for the apis that are already public and stable. Any released api supports +// backward compatibility (i.e. No breaking changes will be introduced for those apis.) +static constexpr uint32_t RELEASED_MATERIAL_API_LEVEL = 1; +// The level used for the apis that are currently under development or development has completed +// but may introduce breaking changes. +static constexpr uint32_t UNSTABLE_MATERIAL_API_LEVEL = 2; /** * Supported shading models @@ -203,7 +212,7 @@ enum class ReflectionMode : uint8_t { // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; -static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 31; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -230,8 +239,12 @@ enum class Property : uint8_t { ABSORPTION, //!< float3, how much light is absorbed by the material TRANSMISSION, //!< float, how much light is refracted through the material IOR, //!< float, material's index of refraction + DISPERSION, //!< float, material's dispersion MICRO_THICKNESS, //!< float, thickness of the thin layer BENT_NORMAL, //!< float3, all shading models only, except unlit + SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth + SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth + SHADOW_STRENGTH, //!< float, [0, 1] strength of shadows received by this material // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT }; @@ -255,4 +268,7 @@ enum class UserVariantFilterBit : UserVariantFilterMask { template<> struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableIntegerOperators + : public std::true_type {}; + #endif diff --git a/package/android/libs/filament/include/filament/MaterialInstance.h b/package/android/libs/filament/include/filament/MaterialInstance.h index 7252f9a4..b052c8f7 100644 --- a/package/android/libs/filament/include/filament/MaterialInstance.h +++ b/package/android/libs/filament/include/filament/MaterialInstance.h @@ -19,7 +19,7 @@ #include #include - +#include #include #include @@ -56,35 +56,36 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { }; public: - using CullingMode = filament::backend::CullingMode; + using CullingMode = backend::CullingMode; + // ReSharper disable once CppRedundantQualifier using TransparencyMode = filament::TransparencyMode; - using DepthFunc = filament::backend::SamplerCompareFunc; - using StencilCompareFunc = filament::backend::SamplerCompareFunc; - using StencilOperation = filament::backend::StencilOperation; - using StencilFace = filament::backend::StencilFace; + using DepthFunc = backend::SamplerCompareFunc; + using StencilCompareFunc = backend::SamplerCompareFunc; + using StencilOperation = backend::StencilOperation; + using StencilFace = backend::StencilFace; template - using is_supported_parameter_t = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || + using is_supported_parameter_t = std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || // these types are slower as they need a layout conversion - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value - >::type; + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v + >; /** * Creates a new MaterialInstance using another MaterialInstance as a template for initialization. @@ -121,13 +122,13 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { /** inline helper to provide the name as a null-terminated string literal */ template> - inline void setParameter(StringLiteral name, T const& value) { + void setParameter(StringLiteral const name, T const& value) { setParameter(name.data, name.size, value); } /** inline helper to provide the name as a null-terminated C string */ template> - inline void setParameter(const char* UTILS_NONNULL name, T const& value) { + void setParameter(const char* UTILS_NONNULL name, T const& value) { setParameter(name, strlen(name), value); } @@ -140,6 +141,7 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { * @param values Array of values to set to the named parameter array. * @param count Size of the array to set. * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + * @see Material::hasParameter */ template> void setParameter(const char* UTILS_NONNULL name, size_t nameLength, @@ -147,14 +149,14 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { /** inline helper to provide the name as a null-terminated string literal */ template> - inline void setParameter(StringLiteral name, const T* UTILS_NONNULL values, size_t count) { + void setParameter(StringLiteral const name, const T* UTILS_NONNULL values, size_t const count) { setParameter(name.data, name.size, values, count); } /** inline helper to provide the name as a null-terminated C string */ template> - inline void setParameter(const char* UTILS_NONNULL name, - const T* UTILS_NONNULL values, size_t count) { + void setParameter(const char* UTILS_NONNULL name, + const T* UTILS_NONNULL values, size_t const count) { setParameter(name, strlen(name), values, count); } @@ -175,14 +177,14 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler); /** inline helper to provide the name as a null-terminated string literal */ - inline void setParameter(StringLiteral name, - Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { + void setParameter(StringLiteral const name, + Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { setParameter(name.data, name.size, texture, sampler); } /** inline helper to provide the name as a null-terminated C string */ - inline void setParameter(const char* UTILS_NONNULL name, - Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { + void setParameter(const char* UTILS_NONNULL name, + Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { setParameter(name, strlen(name), texture, sampler); } @@ -201,12 +203,12 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { RgbType type, math::float3 color); /** inline helper to provide the name as a null-terminated string literal */ - inline void setParameter(StringLiteral name, RgbType type, math::float3 color) { + void setParameter(StringLiteral const name, RgbType const type, math::float3 const color) { setParameter(name.data, name.size, type, color); } /** inline helper to provide the name as a null-terminated C string */ - inline void setParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) { + void setParameter(const char* UTILS_NONNULL name, RgbType const type, math::float3 const color) { setParameter(name, strlen(name), type, color); } @@ -225,34 +227,38 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { RgbaType type, math::float4 color); /** inline helper to provide the name as a null-terminated string literal */ - inline void setParameter(StringLiteral name, RgbaType type, math::float4 color) { + void setParameter(StringLiteral const name, RgbaType const type, math::float4 const color) { setParameter(name.data, name.size, type, color); } /** inline helper to provide the name as a null-terminated C string */ - inline void setParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) { + void setParameter(const char* UTILS_NONNULL name, RgbaType const type, math::float4 const color) { setParameter(name, strlen(name), type, color); } /** * Gets the value of a parameter by name. * + * Note: Only supports non-texture parameters such as numeric and math types. + * * @param name Name of the parameter as defined by Material. Cannot be nullptr. * @param nameLength Length in `char` of the name parameter. * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + * + * @see Material::hasParameter */ template T getParameter(const char* UTILS_NONNULL name, size_t nameLength) const; /** inline helper to provide the name as a null-terminated C string */ template> - inline T getParameter(StringLiteral name) const { + T getParameter(StringLiteral const name) const { return getParameter(name.data, name.size); } /** inline helper to provide the name as a null-terminated C string */ template> - inline T getParameter(const char* UTILS_NONNULL name) const { + T getParameter(const char* UTILS_NONNULL name) const { return getParameter(name, strlen(name)); } @@ -371,11 +377,22 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { */ void setCullingMode(CullingMode culling) noexcept; + /** + * Overrides the default triangle culling state that was set on the material separately for the + * color and shadow passes + */ + void setCullingMode(CullingMode colorPassCullingMode, CullingMode shadowPassCullingMode) noexcept; + /** * Returns the face culling mode. */ CullingMode getCullingMode() const noexcept; + /** + * Returns the face culling mode for the shadow passes. + */ + CullingMode getShadowCullingMode() const noexcept; + /** * Overrides the default color-buffer write state that was set on the material. */ @@ -513,6 +530,13 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { void setStencilWriteMask(uint8_t writeMask, StencilFace face = StencilFace::FRONT_AND_BACK) noexcept; + /** + * PostProcess and compute domain material instance must be commited manually. This call has + * no effect on surface domain materials. + * @param engine Filament engine + */ + void commit(Engine& engine) const; + protected: // prevent heap allocation ~MaterialInstance() = default; diff --git a/package/android/libs/filament/include/filament/MorphTargetBuffer.h b/package/android/libs/filament/include/filament/MorphTargetBuffer.h index 655bb8d8..594f1a6c 100644 --- a/package/android/libs/filament/include/filament/MorphTargetBuffer.h +++ b/package/android/libs/filament/include/filament/MorphTargetBuffer.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -39,7 +40,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI { struct BuilderDetails; public: - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -63,6 +64,33 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI { */ Builder& count(size_t count) noexcept; + /** + * Associate an optional name with this MorphTargetBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this MorphTargetBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this MorphTargetBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this MorphTargetBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the MorphTargetBuffer object and returns a pointer to it. * diff --git a/package/android/libs/filament/include/filament/Options.h b/package/android/libs/filament/include/filament/Options.h index 3966053e..422a0222 100644 --- a/package/android/libs/filament/include/filament/Options.h +++ b/package/android/libs/filament/include/filament/Options.h @@ -70,8 +70,8 @@ enum class BlendMode : uint8_t { * * \note * Dynamic resolution is only supported on platforms where the time to render - * a frame can be measured accurately. Dynamic resolution is currently only - * supported on Android. + * a frame can be measured accurately. On platforms where this is not supported, + * Dynamic Resolution can't be enabled unless minScale == maxScale. * * @see Renderer::FrameRateOptions * @@ -86,12 +86,15 @@ struct DynamicResolutionOptions { /** * Upscaling quality * LOW: bilinear filtered blit. Fastest, poor quality - * MEDIUM: AMD FidelityFX FSR1 w/ mobile optimizations + * MEDIUM: Qualcomm Snapdragon Game Super Resolution (SGSR) 1.0 * HIGH: AMD FidelityFX FSR1 w/ mobile optimizations * ULTRA: AMD FidelityFX FSR1 - * FSR1 require a well anti-aliased (MSAA or TAA), noise free scene. + * FSR1 and SGSR require a well anti-aliased (MSAA or TAA), noise free scene. + * Avoid FXAA and dithering. * * The default upscaling quality is set to LOW. + * + * caveat: currently, 'quality' is always set to LOW if the View is TRANSLUCENT. */ QualityLevel quality = QualityLevel::LOW; }; @@ -164,7 +167,9 @@ struct BloomOptions { }; /** - * Options to control large-scale fog in the scene + * Options to control large-scale fog in the scene. Materials can enable the `linearFog` property, + * which uses a simplified, linear equation for fog calculation; in this mode, the heightFalloff + * is ignored as well as the mipmap selection in IBL or skyColor mode. */ struct FogOptions { /** @@ -183,7 +188,7 @@ struct FogOptions { float cutOffDistance = INFINITY; /** - * fog's maximum opacity between 0 and 1 + * fog's maximum opacity between 0 and 1. Ignored in `linearFog` mode. */ float maximumOpacity = 1.0f; @@ -193,12 +198,15 @@ struct FogOptions { float height = 0.0f; /** - * How fast the fog dissipates with altitude. heightFalloff has a unit of [1/m]. + * How fast the fog dissipates with the altitude. heightFalloff has a unit of [1/m]. * It can be expressed as 1/H, where H is the altitude change in world units [m] that causes a * factor 2.78 (e) change in fog density. * * A falloff of 0 means the fog density is constant everywhere and may result is slightly * faster computations. + * + * In `linearFog` mode, only use to compute the slope of the linear equation. Completely + * ignored if set to 0. */ float heightFalloff = 1.0f; @@ -220,7 +228,7 @@ struct FogOptions { LinearColor color = { 1.0f, 1.0f, 1.0f }; /** - * Extinction factor in [1/m] at altitude 'height'. The extinction factor controls how much + * Extinction factor in [1/m] at an altitude 'height'. The extinction factor controls how much * light is absorbed and out-scattered per unit of distance. Each unit of extinction reduces * the incoming light to 37% of its original value. * @@ -229,11 +237,16 @@ struct FogOptions { * the composition of the fog/atmosphere. * * For historical reason this parameter is called `density`. + * + * In `linearFog` mode this is the slope of the linear equation if heightFalloff is set to 0. + * Otherwise, heightFalloff affects the slope calculation such that it matches the slope of + * the standard equation at the camera height. */ float density = 0.1f; /** * Distance in world units [m] from the camera where the Sun in-scattering starts. + * Ignored in `linearFog` mode. */ float inScatteringStart = 0.0f; @@ -242,6 +255,7 @@ struct FogOptions { * is scattered (by the fog) towards the camera. * Size of the Sun in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100). * Smaller values result is a larger scattering size. + * Ignored in `linearFog` mode. */ float inScatteringSize = -1.0f; @@ -269,6 +283,8 @@ struct FogOptions { * * `fogColorFromIbl` is ignored when skyTexture is specified. * + * In `linearFog` mode mipmap level 0 is always used. + * * @see Texture * @see fogColorFromIbl */ @@ -283,7 +299,7 @@ struct FogOptions { /** * Options to control Depth of Field (DoF) effect in the scene. * - * cocScale can be used to set the depth of field blur independently from the camera + * cocScale can be used to set the depth of field blur independently of the camera * aperture, e.g. for artistic reasons. This can be achieved by setting: * cocScale = cameraAperture / desiredDoFAperture * @@ -373,18 +389,30 @@ struct RenderQuality { * @see setAmbientOcclusionOptions() */ struct AmbientOcclusionOptions { + enum class AmbientOcclusionType : uint8_t { + SAO, //!< use Scalable Ambient Occlusion + GTAO, //!< use Ground Truth-Based Ambient Occlusion + }; + + AmbientOcclusionType aoType = AmbientOcclusionType::SAO;//!< Type of ambient occlusion algorithm. float radius = 0.3f; //!< Ambient Occlusion radius in meters, between 0 and ~10. float power = 1.0f; //!< Controls ambient occlusion's contrast. Must be positive. - float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. + + /** + * Self-occlusion bias in meters. Use to avoid self-occlusion. + * Between 0 and a few mm. No effect when aoType set to GTAO + */ + float bias = 0.0005f; + float resolution = 0.5f;//!< How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0. float intensity = 1.0f; //!< Strength of the Ambient Occlusion effect. float bilateralThreshold = 0.05f; //!< depth distance that constitute an edge for filtering - QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO. - QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness + QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO and params for filtering + QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO. QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality bool enabled = false; //!< enables or disables screen-space ambient occlusion bool bentNormals = false; //!< enables bent normals computation from AO, and specular AO - float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider + float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider. No effect when aoType set to GTAO. /** * Screen Space Cone Tracing (SSCT) options * Ambient shadows from dominant light @@ -402,6 +430,29 @@ struct AmbientOcclusionOptions { bool enabled = false; //!< enables or disables SSCT }; Ssct ssct; // %codegen_skip_javascript% %codegen_java_flatten% + + /** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ + struct Gtao { + uint8_t sampleSliceCount = 4; //!< # of slices. Higher value makes less noise. + uint8_t sampleStepsPerSlice = 3; //!< # of steps the radius is divided into for integration. Higher value makes less bias. + float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0. No effect when useVisibilityBitmasks sets to true. + + /** + * Enables or disables visibility bitmasks mode. Notes that bent normal doesn't work under this mode. + * Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation. + */ + bool useVisibilityBitmasks = false; + float constThickness = 0.5f; //!< constant thickness value of objects on the screen in world space. Only take effect when useVisibilityBitmasks is set to true. + + /** + * Increase thickness with distance to maintain detail on distant surfaces. + * Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation. + */ + bool linearThickness = false; + }; + Gtao gtao; // %codegen_skip_javascript% %codegen_java_flatten% }; /** @@ -438,16 +489,15 @@ struct MultiSampleAntiAliasingOptions { * @see setTemporalAntiAliasingOptions() */ struct TemporalAntiAliasingOptions { - float filterWidth = 1.0f; //!< reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother) + float filterWidth = 1.0f; //!< @deprecated has no effect. float feedback = 0.12f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA). float lodBias = -1.0f; //!< texturing lod bias (typically -1 or -2) float sharpness = 0.0f; //!< post-TAA sharpen, especially useful when upscaling is true. bool enabled = false; //!< enables or disables temporal anti-aliasing - bool upscaling = false; //!< 4x TAA upscaling. Disables Dynamic Resolution. [BETA] + float upscaling = 1.0f; //!< Upscaling factor. Disables Dynamic Resolution. [BETA] enum class BoxType : uint8_t { AABB, //!< use an AABB neighborhood - VARIANCE, //!< use the variance of the neighborhood (not recommended) AABB_VARIANCE //!< use both AABB and variance }; @@ -468,6 +518,7 @@ struct TemporalAntiAliasingOptions { bool filterHistory = true; //!< whether to filter the history buffer bool filterInput = true; //!< whether to apply the reconstruction filter to the input bool useYCoCg = false; //!< whether to use the YcoCg color-space for history rejection + bool hdr = true; //!< set to true for HDR content BoxType boxType = BoxType::AABB; //!< type of color gamut box BoxClipping boxClipping = BoxClipping::ACCURATE; //!< clipping algorithm JitterPattern jitterPattern = JitterPattern::HALTON_23_X16; //! Jitter Pattern diff --git a/package/android/libs/filament/include/filament/RenderTarget.h b/package/android/libs/filament/include/filament/RenderTarget.h index fc76111d..02eb6714 100644 --- a/package/android/libs/filament/include/filament/RenderTarget.h +++ b/package/android/libs/filament/include/filament/RenderTarget.h @@ -81,7 +81,7 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { }; //! Use Builder to construct a RenderTarget object instance - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -118,7 +118,7 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { Builder& mipLevel(AttachmentPoint attachment, uint8_t level) noexcept; /** - * Sets the cubemap face for a given attachment point. + * Sets the face for cubemap textures at the given attachment point. * * @param attachment The attachment point. * @param face The associated cubemap face. @@ -127,7 +127,12 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { Builder& face(AttachmentPoint attachment, CubemapFace face) noexcept; /** - * Sets the layer for a given attachment point (for 3D textures). + * Sets an index of a single layer for 2d array, cubemap array, and 3d textures at the given + * attachment point. + * + * For cubemap array textures, layer is translated into an array index and face according to + * - index: layer / 6 + * - face: layer % 6 * * @param attachment The attachment point. * @param layer The associated cubemap layer. @@ -135,6 +140,27 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { */ Builder& layer(AttachmentPoint attachment, uint32_t layer) noexcept; + /** + * Sets the starting index of the 2d array textures for multiview at the given attachment + * point. + * + * This requires COLOR and DEPTH attachments (if set) to be of 2D array textures. + * + * @param attachment The attachment point. + * @param layerCount The number of layers used for multiview, starting from baseLayer. + * @param baseLayer The starting index of the 2d array texture. + * @return A reference to this Builder for chaining calls. + */ + Builder& multiview(AttachmentPoint attachment, uint8_t layerCount, uint8_t baseLayer = 0) noexcept; + + /** + * Sets the number of samples used for MSAA (Multisample Anti-Aliasing). + * + * @param samples The number of samples used for multisampling. + * @return A reference to this Builder for chaining calls. + */ + Builder& samples(uint8_t samples) noexcept; + /** * Creates the RenderTarget object and returns a pointer to it. * diff --git a/package/android/libs/filament/include/filament/RenderableManager.h b/package/android/libs/filament/include/filament/RenderableManager.h index bb50b7d1..1428ed1e 100644 --- a/package/android/libs/filament/include/filament/RenderableManager.h +++ b/package/android/libs/filament/include/filament/RenderableManager.h @@ -220,7 +220,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * the renderable are immutable. * STATIC geometry has the same restrictions as STATIC_BOUNDS, but in addition disallows * skinning, morphing and changing the VertexBuffer or IndexBuffer in any way. - * @param enable whether this renderable has static bounds. false by default. + * @param type type of geometry. */ Builder& geometryType(GeometryType type) noexcept; @@ -297,7 +297,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& priority(uint8_t priority) noexcept; /** - * Set the channel this renderable is associated to. There can be 4 channels. + * Set the channel this renderable is associated to. There can be 8 channels. * All renderables in a given channel are rendered together, regardless of anything else. * They are sorted as usual within a channel. * Channels work similarly to priorities, except that they enforce the strongest ordering. @@ -305,7 +305,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * Channels 0 and 1 may not have render primitives using a material with `refractionType` * set to `screenspace`. * - * @param channel clamped to the range [0..3], defaults to 2. + * @param channel clamped to the range [0..7], defaults to 2. * * @return Builder reference for chaining calls. * @@ -454,7 +454,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * * @param primitiveIndex zero-based index of the primitive, must be less than the primitive * count passed to Builder constructor - * @param indicesAndWeightsVectors pairs of bone index and bone weight for all vertices of + * @param indicesAndWeightsVector pairs of bone index and bone weight for all vertices of * the primitive sequentially * * @return Builder reference for chaining calls. @@ -464,16 +464,11 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& boneIndicesAndWeights(size_t primitiveIndex, utils::FixedCapacityVector< utils::FixedCapacityVector> indicesAndWeightsVector) noexcept; + /** - * Controls if the renderable has vertex morphing targets, zero by default. This is + * Controls if the renderable has legacy vertex morphing targets, zero by default. This is * required to enable GPU morphing. * - * Filament supports two morphing modes: standard (default) and legacy. - * - * For standard morphing, A MorphTargetBuffer must be created and provided via - * RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to - * \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. - * * For legacy morphing, the attached VertexBuffer must provide data in the * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must @@ -486,26 +481,29 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& morphing(size_t targetCount) noexcept; /** - * Specifies the morph target buffer for a primitive. + * Controls if the renderable has vertex morphing targets, zero by default. This is + * required to enable GPU morphing. * - * The morph target buffer must have an associated renderable and geometry. Two conditions - * must be met: - * 1. The number of morph targets in the buffer must equal the renderable's morph target - * count. - * 2. The vertex count of each morph target must equal the geometry's vertex count. + * Filament supports two morphing modes: standard (default) and legacy. + * + * For standard morphing, A MorphTargetBuffer must be provided. + * Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. + * + * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis + * to advance the animation. + */ + Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; + + /** + * Specifies the the range of the MorphTargetBuffer to use with this primitive. * * @param level the level of detail (lod), only 0 can be specified * @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor - * @param morphTargetBuffer specifies the morph target buffer * @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices) - * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3) */ - Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, - size_t offset, size_t count) noexcept; + Builder& morphing(uint8_t level, + size_t primitiveIndex, size_t offset) noexcept; - inline Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; /** * Sets the drawing order for blended primitives. The drawing order is either global or @@ -599,21 +597,6 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { friend class FEngine; friend class FRenderPrimitive; friend class FRenderableManager; - struct Entry { - VertexBuffer* UTILS_NULLABLE vertices = nullptr; - IndexBuffer* UTILS_NULLABLE indices = nullptr; - size_t offset = 0; - size_t count = 0; - MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr; - PrimitiveType type = PrimitiveType::TRIANGLES; - uint16_t blendOrder = 0; - bool globalBlendOrderEnabled = false; - struct { - MorphTargetBuffer* UTILS_NULLABLE buffer = nullptr; - size_t offset = 0; - size_t count = 0; - } morphing; - }; }; /** @@ -765,20 +748,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /** * Associates a MorphTargetBuffer to the given primitive. */ - void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count); - - /** - * Utility method to change a MorphTargetBuffer to the given primitive - */ - inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer); + void setMorphTargetBufferOffsetAt(Instance instance, uint8_t level, size_t primitiveIndex, + size_t offset); /** - * Get a MorphTargetBuffer to the given primitive or null if it doesn't exist. + * Get a MorphTargetBuffer to the given renderable or null if it doesn't exist. */ - MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance, - uint8_t level, size_t primitiveIndex) const noexcept; + MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBuffer(Instance instance) const noexcept; /** * Gets the number of morphing in the given entity. @@ -807,6 +783,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { */ size_t getPrimitiveCount(Instance instance) const noexcept; + /** + * Returns the number of instances for this renderable. + * @param instance Instance of the component obtained from getInstance(). + * @return The number of instances. + */ + size_t getInstanceCount(Instance instance) const noexcept; + /** * Changes the material instance binding for the given primitive. * @@ -822,6 +805,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { void setMaterialInstanceAt(Instance instance, size_t primitiveIndex, MaterialInstance const* UTILS_NONNULL materialInstance); + /** + * Clear the MaterialInstance for the given primitive. + * @param instance Renderable's instance + * @param primitiveIndex Primitive index + */ + void clearMaterialInstanceAt(Instance instance, size_t primitiveIndex); + /** * Retrieves the material instance that is bound to the given primitive. */ @@ -869,20 +859,20 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /*! \cond PRIVATE */ template struct is_supported_vector_type { - using type = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value - >::type; + using type = std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v + >; }; template struct is_supported_index_type { - using type = typename std::enable_if< - std::is_same::value || - std::is_same::value - >::type; + using type = std::enable_if_t< + std::is_same_v || + std::is_same_v + >; }; /*! \endcond */ @@ -906,20 +896,6 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { ~RenderableManager() = default; }; -RenderableManager::Builder& RenderableManager::Builder::morphing( - uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept { - return morphing(level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); -} - -void RenderableManager::setMorphTargetBufferAt( - Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) { - setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); -} - template Box RenderableManager::computeAABB( VECTOR const* UTILS_NONNULL vertices, diff --git a/package/android/libs/filament/include/filament/Renderer.h b/package/android/libs/filament/include/filament/Renderer.h index fdc291b1..53c13cab 100644 --- a/package/android/libs/filament/include/filament/Renderer.h +++ b/package/android/libs/filament/include/filament/Renderer.h @@ -22,9 +22,11 @@ #include #include +#include #include +#include #include namespace filament { @@ -81,6 +83,50 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { UTILS_DEPRECATED uint64_t vsyncOffsetNanos = 0; }; + /** + * Timing information about a frame + * @see getFrameInfoHistory() + */ + struct FrameInfo { + /** duration in nanosecond since epoch of std::steady_clock */ + using time_point_ns = int64_t; + /** duration in nanosecond on the std::steady_clock */ + using duration_ns = int64_t; + static constexpr time_point_ns INVALID = -1; //!< value not supported + static constexpr time_point_ns PENDING = -2; //!< value not yet available + uint32_t frameId; //!< monotonically increasing frame identifier + duration_ns gpuFrameDuration; //!< frame duration on the GPU in nanosecond [ns] + duration_ns denoisedGpuFrameDuration; //!< denoised frame duration on the GPU in [ns] + time_point_ns beginFrame; //!< Renderer::beginFrame() time since epoch [ns] + time_point_ns endFrame; //!< Renderer::endFrame() time since epoch [ns] + time_point_ns backendBeginFrame; //!< Backend thread time of frame start since epoch [ns] + time_point_ns backendEndFrame; //!< Backend thread time of frame end since epoch [ns] + time_point_ns gpuFrameComplete; //!< GPU thread time of frame end since epoch [ns] or 0 + time_point_ns vsync; //!< VSYNC time of this frame since epoch [ns] + time_point_ns displayPresent; //!< Actual presentation time of this frame since epoch [ns] + time_point_ns presentDeadline; //!< deadline for queuing a frame [ns] + duration_ns displayPresentInterval; //!< display refresh rate [ns] + duration_ns compositionToPresentLatency; //!< time between the start of composition and the expected present time [ns] + time_point_ns expectedPresentTime; //!< system's expected presentation time since epoch [ns] + }; + + /** + * Retrieve a history of frame timing information. The maximum frame history size is + * given by getMaxFrameHistorySize(). + * All or part of the history can be lost when using a different SwapChain in beginFrame(). + * @param historySize requested history size. The returned vector could be smaller. + * @return A vector of FrameInfo. + * @see beginFrame() + */ + utils::FixedCapacityVector getFrameInfoHistory( + size_t historySize = 1) const noexcept; + + /** + * @return the maximum supported frame history size. + * @see getFrameInfoHistory() + */ + size_t getMaxFrameHistorySize() const noexcept; + /** * Use FrameRateOptions to set the desired frame rate and control how quickly the system * reacts to GPU load changes. @@ -228,9 +274,42 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { /** - * Set-up a frame for this Renderer. + * The use of this method is optional. It sets the VSYNC time expressed as the duration in + * nanosecond since epoch of std::chrono::steady_clock. + * If called, passing 0 to vsyncSteadyClockTimeNano in Renderer::BeginFrame will use this + * time instead. + * @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock + * @see Engine::getSteadyClockTimeNano() + * @see Renderer::BeginFrame() + */ + void setVsyncTime(uint64_t steadyClockTimeNano) noexcept; + + /** + * Call skipFrame when momentarily skipping frames, for instance if the content of the + * scene doesn't change. + * + * @param vsyncSteadyClockTimeNano + */ + void skipFrame(uint64_t vsyncSteadyClockTimeNano = 0u); + + /** + * Returns true if the current frame should be rendered. + * + * This is a convenience method that returns the same value as beginFrame(). + * + * @return + * *false* the current frame should be skipped, + * *true* the current frame can be rendered + * + * @see + * beginFrame() + */ + bool shouldRenderFrame() const noexcept; + + /** + * Set up a frame for this Renderer. * - * beginFrame() manages frame pacing, and returns whether or not a frame should be drawn. The + * beginFrame() manages frame-pacing, and returns whether a frame should be drawn. The * goal of this is to skip frames when the GPU falls behind in order to keep the frame * latency low. * @@ -249,6 +328,8 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * or 0 if unknown. This value should be the timestamp of * the last h/w vsync. It is expressed in the * std::chrono::steady_clock time base. + * On Android this should be the frame time received from + * a Choreographer. * @param swapChain A pointer to the SwapChain instance to use. * * @return @@ -260,6 +341,8 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * * @note * All calls to render() must happen *after* beginFrame(). + * It is recommended to use the same swapChain for every call to beginFrame, failing to do + * so can result is losing all or part of the FrameInfo history. * * @see * endFrame() @@ -456,7 +539,7 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * * Framebuffer as seen on User buffer (PixelBufferDescriptor&) * screen - * + * * +--------------------+ * | | .stride .alignment * | | ----------------------->--> @@ -486,6 +569,9 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * uploaded to it via setImage, the data returned from readPixels will be y-flipped with respect * to the setImage call. * + * Note: the texture that backs the COLOR attachment for `renderTarget` must have + * TextureUsage::BLIT_SRC as part of its usage. + * * @remark * readPixels() is intended for debugging and testing. It will impact performance significantly. * @@ -577,6 +663,19 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { */ void resetUserTime(); + + /** + * Requests the next frameCount frames to be skipped. For Debugging. + * @param frameCount number of frames to skip. + */ + void skipNextFrames(size_t frameCount) const noexcept; + + /** + * Remainder count of frame to be skipped + * @return remaining frames to be skipped + */ + size_t getFrameToSkipCount() const noexcept; + protected: // prevent heap allocation ~Renderer() = default; diff --git a/package/android/libs/filament/include/filament/Scene.h b/package/android/libs/filament/include/filament/Scene.h index 9df6285c..8758ab70 100644 --- a/package/android/libs/filament/include/filament/Scene.h +++ b/package/android/libs/filament/include/filament/Scene.h @@ -141,6 +141,11 @@ class UTILS_PUBLIC Scene : public FilamentAPI { */ void removeEntities(const utils::Entity* UTILS_NONNULL entities, size_t count); + /** + * Remove all entities to the Scene. + */ + void removeAllEntities() noexcept; + /** * Returns the total number of Entities in the Scene, whether alive or not. * @return Total number of Entities in the Scene. diff --git a/package/android/libs/filament/include/filament/SkinningBuffer.h b/package/android/libs/filament/include/filament/SkinningBuffer.h index 36ae30ed..c397f62b 100644 --- a/package/android/libs/filament/include/filament/SkinningBuffer.h +++ b/package/android/libs/filament/include/filament/SkinningBuffer.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -39,7 +40,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI { struct BuilderDetails; public: - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -69,6 +70,33 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI { */ Builder& initialize(bool initialize = true) noexcept; + /** + * Associate an optional name with this SkinningBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this SkinningBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this SkinningBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this SkinningBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the SkinningBuffer object and returns a pointer to it. * diff --git a/package/android/libs/filament/include/filament/Skybox.h b/package/android/libs/filament/include/filament/Skybox.h index ce203aae..bacd0acb 100644 --- a/package/android/libs/filament/include/filament/Skybox.h +++ b/package/android/libs/filament/include/filament/Skybox.h @@ -131,6 +131,20 @@ class UTILS_PUBLIC Skybox : public FilamentAPI { */ Builder& color(math::float4 color) noexcept; + /** + * Set the rendering priority of the Skybox. By default, it is set to the lowest + * priority (7) such that the Skybox is always rendered after the opaque objects, + * to reduce overdraw when depth culling is enabled. + * + * @param priority clamped to the range [0..7], defaults to 4; 7 is lowest priority + * (rendered last). + * + * @return Builder reference for chaining calls. + * + * @see RenderableManager::Builder::priority() + */ + Builder& priority(uint8_t priority) noexcept; + /** * Creates the Skybox object and returns a pointer to it. * diff --git a/package/android/libs/filament/include/filament/Stream.h b/package/android/libs/filament/include/filament/Stream.h index 6cafbacc..d3f914eb 100644 --- a/package/android/libs/filament/include/filament/Stream.h +++ b/package/android/libs/filament/include/filament/Stream.h @@ -23,6 +23,9 @@ #include #include +#include + +#include #include @@ -94,7 +97,7 @@ class UTILS_PUBLIC Stream : public FilamentAPI { * * To create a NATIVE stream, call the

stream
method on the builder. */ - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -136,6 +139,33 @@ class UTILS_PUBLIC Stream : public FilamentAPI { */ Builder& height(uint32_t height) noexcept; + /** + * Associate an optional name with this Stream for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this Stream + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this Stream for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this Stream + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the Stream object and returns a pointer to it. * @@ -174,9 +204,10 @@ class UTILS_PUBLIC Stream : public FilamentAPI { * The 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. + * @param transform Optional transform matrix to apply to the image. */ void setAcquiredImage(void* UTILS_NONNULL image, - Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept; + Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata, math::mat3f const& transform = math::mat3f()) noexcept; /** * @see setAcquiredImage(void*, Callback, void*) @@ -187,10 +218,11 @@ class UTILS_PUBLIC Stream : public FilamentAPI { * 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. + * @param transform Optional transform matrix to apply to the image. */ void setAcquiredImage(void* UTILS_NONNULL image, backend::CallbackHandler* UTILS_NULLABLE handler, - Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept; + Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata, math::mat3f const& transform = math::mat3f()) noexcept; /** * Updates the size of the incoming stream. Whether this value is used is diff --git a/package/android/libs/filament/include/filament/SwapChain.h b/package/android/libs/filament/include/filament/SwapChain.h index 0af01afc..94721c0d 100644 --- a/package/android/libs/filament/include/filament/SwapChain.h +++ b/package/android/libs/filament/include/filament/SwapChain.h @@ -34,7 +34,7 @@ class Engine; /** * A swap chain represents an Operating System's *native* renderable surface. * - * Typically it's a native window or a view. Because a SwapChain is initialized from a + * Typically, it's a native window or a view. Because a SwapChain is initialized from a * native object, it is given to filament as a `void *`, which must be of the proper type * for each platform filament is running on. * @@ -115,7 +115,7 @@ class Engine; * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * SDL_SysWMinfo wmi; * SDL_VERSION(&wmi.version); - * ASSERT_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi), "SDL version unsupported!"); + * FILAMENT_CHECK_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi)) << "SDL version unsupported!"; * HDC nativeWindow = (HDC) wmi.info.win.hdc; * * using namespace filament; @@ -157,7 +157,7 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { /** * Requests a SwapChain with an alpha channel. */ - static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT; + static constexpr uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT; /** * This flag indicates that the swap chain may be used as a source surface @@ -167,13 +167,13 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { * @see * Renderer.copyFrame() */ - static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE; + static constexpr uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE; /** * Indicates that the native X11 window is an XCB window rather than an XLIB window. * This is ignored on non-Linux platforms and in builds that support only one X11 API. */ - static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB; + static constexpr uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB; /** * Indicates that the native window is a CVPixelBufferRef. @@ -185,7 +185,7 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { * Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and * CVPixelBufferRelease when the swap chain is destroyed. */ - static const uint64_t CONFIG_APPLE_CVPIXELBUFFER = + static constexpr uint64_t CONFIG_APPLE_CVPIXELBUFFER = backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER; /** @@ -233,6 +233,21 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { */ static constexpr uint64_t CONFIG_PROTECTED_CONTENT = backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT; + /** + * Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the + * given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is + * true. + * + * This is supported by EGL(Android) and Metal. Other GL platforms (GLX, WGL, etc) don't support + * it because the swapchain MSAA settings must be configured before window creation. + * + * With Metal, this flag should only be used when rendering a single View into a SwapChain. This + * flag is not supported when rendering multiple Filament Views into this SwapChain. + * + * @see isMSAASwapChainSupported(4) + */ + static constexpr uint64_t CONFIG_MSAA_4_SAMPLES = backend::SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES; + /** * Return whether createSwapChain supports the CONFIG_PROTECTED_CONTENT flag. * The default implementation returns false. @@ -251,55 +266,97 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { */ static bool isSRGBSwapChainSupported(Engine& engine) noexcept; + /** + * Return whether createSwapChain supports the CONFIG_MSAA_*_SAMPLES flag. + * The default implementation returns false. + * + * @param engine A pointer to the filament Engine + * @param samples The number of samples + * @return true if CONFIG_MSAA_*_SAMPLES is supported, false otherwise. + */ + static bool isMSAASwapChainSupported(Engine& engine, uint32_t samples) noexcept; + void* UTILS_NULLABLE getNativeWindow() const noexcept; /** - * FrameScheduledCallback is a callback function that notifies an application when Filament has - * completed processing a frame and that frame is ready to be scheduled for presentation. + * If this flag is passed to setFrameScheduledCallback, then the behavior of the default + * CallbackHandler (when nullptr is passed as the handler argument) is altered to call the + * callback on the Metal completion handler thread (as opposed to the main Filament thread). + * This flag also instructs the Metal backend to release the associated CAMetalDrawable on the + * completion handler thread. + * + * This flag has no effect if a custom CallbackHandler is passed or on backends other than Metal. + * + * @see setFrameScheduledCallback + */ + static constexpr uint64_t CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER = 1; + + /** + * FrameScheduledCallback is a callback function that notifies an application about the status + * of a frame after Filament has finished its processing. + * + * The exact timing and semantics of this callback differ depending on the graphics backend in + * use. + * + * Metal Backend + * ============= + * + * With the Metal backend, this callback signifies that Filament has completed all CPU-side + * processing for a frame and the frame is ready to be scheduled for presentation. * * Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain. - * If a SwapChain::FrameScheduledCallback is set, however, the application bares the - * responsibility of scheduling a frame for presentation by calling the backend::PresentCallable - * passed to the callback function. Currently this functionality is only supported by the Metal - * backend. + * If a SwapChain::FrameScheduledCallback is set, however, the application bears the + * responsibility of scheduling the frame for presentation by calling the + * backend::PresentCallable passed to the callback function. In this mode, Filament will *not* + * automatically schedule the frame for presentation. * - * A FrameScheduledCallback can be set on an individual SwapChain through - * SwapChain::setFrameScheduledCallback. If the callback is set, then the SwapChain will *not* - * automatically schedule itself for presentation. Instead, the application must call the - * PresentCallable passed to the FrameScheduledCallback. + * When using the Metal backend, if your application delays the call to the PresentCallable + * (e.g., by invoking it on a separate thread), you must ensure all PresentCallables have been + * called before shutting down the Filament Engine. You can guarantee this by calling + * Engine::flushAndWait() before Engine::shutdown(). This is necessary to ensure the Engine has + * a chance to clean up all memory related to frame presentation. + * + * Other Backends (OpenGL, Vulkan, WebGPU) + * ======================================= * - * There may be only one FrameScheduledCallback set per SwapChain. A call to - * SwapChain::setFrameScheduledCallback will overwrite any previous FrameScheduledCallbacks set - * on the same SwapChain. + * On other backends, this callback serves as a notification that Filament has completed all + * CPU-side processing for a frame. Filament proceeds with its normal presentation logic + * automatically, and the PresentCallable passed to the callback is a no-op that can be safely + * ignored. * - * If your application delays the call to the PresentCallable by, for example, calling it on a - * separate thread, you must ensure all PresentCallables have been called before shutting down - * the Filament Engine. You can do this by issuing an Engine::flushAndWait before calling - * Engine::shutdown. This is necessary to ensure the Filament Engine has had a chance to clean - * up all memory related to frame presentation. + * General Behavior + * ================ * - * @param callback A callback, or nullptr to unset. - * @param user An optional pointer to user data passed to the callback function. + * A FrameScheduledCallback can be set on an individual SwapChain through + * SwapChain::setFrameScheduledCallback. Each SwapChain can have only one callback set per + * frame. If setFrameScheduledCallback is called multiple times on the same SwapChain before + * Renderer::endFrame(), the most recent call effectively overwrites any previously set + * callback. + * + * The callback set by setFrameScheduledCallback is "latched" when Renderer::endFrame() is + * executed. At this point, the callback is fixed for the frame that was just encoded. + * Subsequent calls to setFrameScheduledCallback after endFrame() will apply to the next frame. * - * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other - * backends ignore the callback (which will never be called) and proceed normally. + * Use \c setFrameScheduledCallback() (with default arguments) to unset the callback. * - * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. + * @param handler Handler to dispatch the callback or nullptr for the default handler. + * @param callback Callback to be invoked when the frame processing is complete. + * @param flags See CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER * + * @see CallbackHandler * @see PresentCallable */ - void setFrameScheduledCallback(FrameScheduledCallback UTILS_NULLABLE callback, - void* UTILS_NULLABLE user = nullptr); + void setFrameScheduledCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr, + FrameScheduledCallback&& callback = {}, uint64_t flags = 0); /** - * Returns the SwapChain::FrameScheduledCallback that was previously set with - * SwapChain::setFrameScheduledCallback, or nullptr if one is not set. + * Returns whether this SwapChain currently has a FrameScheduledCallback set. * - * @return the previously-set FrameScheduledCallback, or nullptr + * @return true, if the last call to setFrameScheduledCallback set a callback * * @see SwapChain::setFrameCompletedCallback */ - UTILS_NULLABLE FrameScheduledCallback getFrameScheduledCallback() const noexcept; + bool isFrameScheduledCallbackSet() const noexcept; /** * FrameCompletedCallback is a callback function that notifies an application when a frame's diff --git a/package/android/libs/filament/include/filament/Sync.h b/package/android/libs/filament/include/filament/Sync.h new file mode 100644 index 00000000..0398ced6 --- /dev/null +++ b/package/android/libs/filament/include/filament/Sync.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2025 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_SYNC_H +#define TNT_FILAMENT_SYNC_H + +#include + +#include +#include +#include + +namespace filament { + +class UTILS_PUBLIC Sync : public FilamentAPI { +public: + using CallbackHandler = backend::CallbackHandler; + using Callback = backend::Platform::SyncCallback; + + /** + * Fetches a handle to the external, platform-specific representation of + * this sync object. + * + * @param handler A handler for the callback that will receive the handle + * @param callback A callback that will receive the handle when ready + * @param userData Data to be passed to the callback so that the application + * can identify what frame the sync is relevant to. + * @return The external handle for the Sync. This is valid destroy() is + * called on this Sync object. + */ + void getExternalHandle(CallbackHandler* handler, Callback callback, void* userData) noexcept; + +protected: + // prevent heap allocation + ~Sync() = default; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_SYNC_H diff --git a/package/android/libs/filament/include/filament/Texture.h b/package/android/libs/filament/include/filament/Texture.h index 8a27f831..f7d3fcbf 100644 --- a/package/android/libs/filament/include/filament/Texture.h +++ b/package/android/libs/filament/include/filament/Texture.h @@ -23,11 +23,15 @@ #include #include +#include #include +#include +#include #include +#include #include #include @@ -70,7 +74,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { struct BuilderDetails; public: - static constexpr const size_t BASE_LEVEL = 0; + static constexpr size_t BASE_LEVEL = 0; //! Face offsets for all faces of a cubemap struct FaceOffsets; @@ -84,35 +88,42 @@ class UTILS_PUBLIC Texture : public FilamentAPI { using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format using Usage = backend::TextureUsage; //!< Usage affects texel layout using Swizzle = backend::TextureSwizzle; //!< Texture swizzle + using ExternalImageHandle = backend::Platform::ExternalImageHandle; + using ExternalImageHandleRef = backend::Platform::ExternalImageHandleRef; + using AsyncCompletionCallback = + std::function; + using AsyncCallId = backend::AsyncCallId; - /** @return whether a backend supports a particular format. */ + /** @return Whether a backend supports a particular format. */ static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept; - /** @return whether this backend supports protected textures. */ + /** @return Whether a backend supports mipmapping of a particular format. */ + static bool isTextureFormatMipmappable(Engine& engine, InternalFormat format) noexcept; + + /** @return Whether particular format is compressed */ + static bool isTextureFormatCompressed(InternalFormat format) noexcept; + + /** @return Whether this backend supports protected textures. */ static bool isProtectedTexturesSupported(Engine& engine) noexcept; - /** @return whether a backend supports texture swizzling. */ + /** @return Whether a backend supports texture swizzling. */ static bool isTextureSwizzleSupported(Engine& engine) noexcept; - static size_t computeTextureDataSize(Texture::Format format, Texture::Type type, + static size_t computeTextureDataSize(Format format, Type type, size_t stride, size_t height, size_t alignment) noexcept; + /** @return Whether a combination of texture format, pixel format and type is valid. */ + static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept; - /** - * Options for environment prefiltering into reflection map - * - * @see generatePrefilterMipmap() - */ - struct PrefilterOptions { - uint16_t sampleCount = 8; //!< sample count used for filtering - bool mirror = true; //!< whether the environment must be mirrored - private: - UTILS_UNUSED uintptr_t reserved[3] = {}; - }; + /** @return the maximum size in texels of a texture of type \p type. At least 2048 for + * 2D textures, 256 for 3D textures. */ + static size_t getMaxTextureSize(Engine& engine, Sampler type) noexcept; + /** @return the maximum number of layers supported by texture arrays. At least 256. */ + static size_t getMaxArrayTextureLayers(Engine& engine) noexcept; //! Use Builder to construct a Texture object instance - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -156,6 +167,20 @@ class UTILS_PUBLIC Texture : public FilamentAPI { */ Builder& levels(uint8_t levels) noexcept; + /** + * Specifies the numbers of samples used for MSAA (Multisample Anti-Aliasing). + * + * Calling this method implicitly indicates the texture is used as a render target. Hence, + * this method should not be used in conjunction with other methods that are semantically + * conflicting like `setImage`. + * + * If this is invoked for array textures, it means this texture is used for multiview. + * + * @param samples Number of samples for this texture. + * @return This Builder, for chaining calls. + */ + Builder& samples(uint8_t samples) noexcept; + /** * Specifies the type of sampler to use. * @param target Sampler type @@ -202,6 +227,71 @@ class UTILS_PUBLIC Texture : public FilamentAPI { */ Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept; + /** + * Associate an optional name with this Texture for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this Texture + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this Texture for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this Texture + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + + /** + * Creates an external texture. The content must be set using setExternalImage(). + * The sampler can be SAMPLER_EXTERNAL or SAMPLER_2D depending on the format. Generally + * YUV formats must use SAMPLER_EXTERNAL. This depends on the backend features and is not + * validated. + * + * If the Sampler is set to SAMPLER_EXTERNAL, external() is implied. + * + * @return + */ + Builder& external() noexcept; + + /** + * Specifies a callback that will execute once the resource's data has been fully allocated + * within the GPU memory. This enables the resource creation process to be handled + * asynchronously. + * + * Any asynchronous calls made during a resource's asynchronous creation (using this method) + * are safe because they are queued and executed in sequence. However, invoking regular + * methods on the same resource before it's fully ready is unsafe and may cause undefined + * behavior. Users can call the `isCreationComplete()` method for the resource to confirm + * when the resource is ready for regular API calls. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * This method and the `external()` method are mutually exclusive. You cannot use both + * because external texture's contents are filled later by calling `setExternalImage()`. + * + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * @return This Builder, for chaining calls. + */ + Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback = nullptr, + void* UTILS_NULLABLE user = nullptr) noexcept; + /** * Creates the Texture object and returns a pointer to it. * @@ -332,7 +422,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * uint32_t width, uint32_t height, uint32_t depth, * PixelBufferDescriptor&& buffer) */ - inline void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const { + void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const { setImage(engine, level, 0, 0, 0, uint32_t(getWidth(level)), uint32_t(getHeight(level)), 1, std::move(buffer)); } @@ -345,7 +435,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * uint32_t width, uint32_t height, uint32_t depth, * PixelBufferDescriptor&& buffer) */ - inline void setImage(Engine& engine, size_t level, + void setImage(Engine& engine, size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& buffer) const { setImage(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer)); @@ -378,9 +468,108 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const; + /** + * An asynchronous version of `setImage()`. + * Updates a sub-image of a 3D texture or 2D texture array for a level. Cubemaps are treated + * like a 2D array of six layers. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param engine Engine this texture is associated to. + * @param level Level to set the image for. + * @param xoffset Left offset of the sub-region to update. + * @param yoffset Bottom offset of the sub-region to update. + * @param zoffset Depth offset of the sub-region to update. + * @param width Width of the sub-region to update. + * @param height Height of the sub-region to update. + * @param depth Depth of the sub-region to update. + * @param buffer Client-side buffer containing the image to set. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention \p level must be less than getLevels(). + * @attention \p buffer's Texture::Format must match that of getFormat(). + * @attention This Texture instance must use Sampler::SAMPLER_3D, Sampler::SAMPLER_2D_ARRAY + * or Sampler::SAMPLER_CUBEMAP. + * + * @see Builder::sampler() + */ + AsyncCallId setImageAsync(Engine& engine, size_t level, + uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + uint32_t width, uint32_t height, uint32_t depth, + PixelBufferDescriptor&& buffer, + backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr) const; + + /** + * inline helper to update a 2D texture asynchronously + * + * @see setImageAsync(Engine& engine, size_t level, + * uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + * uint32_t width, uint32_t height, uint32_t depth, + * PixelBufferDescriptor&& buffer, + * backend::CallbackHandler* UTILS_NULLABLE handler, + * AsyncCompletionCallback callback, void* user) + */ + AsyncCallId setImageAsync(Engine& engine, size_t level, PixelBufferDescriptor&& buffer, + backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr) const { + return setImageAsync(engine, level, 0, 0, 0, + uint32_t(getWidth(level)), uint32_t(getHeight(level)), 1, std::move(buffer), + handler, std::move(callback), user); + } + + /** + * inline helper to update a 2D texture asynchronously + * + * @see setImageAsync(Engine& engine, size_t level, + * uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + * uint32_t width, uint32_t height, uint32_t depth, + * PixelBufferDescriptor&& buffer, + * backend::CallbackHandler* UTILS_NULLABLE handler, + * AsyncCompletionCallback callback, void* user) + */ + AsyncCallId setImageAsync(Engine& engine, size_t level, + uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, + PixelBufferDescriptor&& buffer, + backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr) const { + return setImageAsync(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer), + handler, std::move(callback), user); + } + + /** + * Specify the external image to associate with this Texture. Typically, the external + * image is OS specific, and can be a video or camera frame. + * There are many restrictions when using an external image as a texture, such as: + * - only the level of detail (lod) 0 can be specified + * - only nearest or linear filtering is supported + * - the size and format of the texture is defined by the external image + * - only the CLAMP_TO_EDGE wrap mode is supported + * + * @param engine Engine this texture is associated to. + * @param image An opaque handle to a platform specific image. It must be created using Platform + * specific APIs. For example PlatformEGL::createExternalImage(EGLImageKHR eglImage) + * + * @see PlatformEGL::createExternalImage + * @see PlatformEGLAndroid::createExternalImage + * @see PlatformCocoaGL::createExternalImage + * @see PlatformCocoaTouchGL::createExternalImage + */ + void setExternalImage(Engine& engine, ExternalImageHandleRef image) noexcept; /** - * Specify the external image to associate with this Texture. Typically the external + * Specify the external image to associate with this Texture. Typically, the external * image is OS specific, and can be a video or camera frame. * There are many restrictions when using an external image as a texture, such as: * - only the level of detail (lod) 0 can be specified @@ -401,11 +590,13 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * * @see Builder::sampler() * + * @deprecated Instead, use setExternalImage(Engine& engine, ExternalImageHandleRef image) */ + UTILS_DEPRECATED void setExternalImage(Engine& engine, void* UTILS_NONNULL image) noexcept; /** - * Specify the external image and plane to associate with this Texture. Typically the external + * Specify the external image and plane to associate with this Texture. Typically, the external * image is OS specific, and can be a video or camera frame. When using this method, the * external image must be a planar type (such as a YUV camera frame). The plane parameter * selects which image plane is bound to this texture. @@ -436,7 +627,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void setExternalImage(Engine& engine, void* UTILS_NONNULL image, size_t plane) noexcept; /** - * Specify the external stream to associate with this Texture. Typically the external + * Specify the external stream to associate with this Texture. Typically, the external * stream is OS specific, and can be a video or camera stream. * There are many restrictions when using an external stream as a texture, such as: * - only the level of detail (lod) 0 can be specified @@ -447,7 +638,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * @param stream A Stream object * * @attention \p engine must be the instance passed to Builder::build() - * @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL or it has no effect + * @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL, or it has no effect * * @see Builder::sampler(), Stream * @@ -467,43 +658,16 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void generateMipmaps(Engine& engine) const noexcept; /** - * Creates a reflection map from an environment map. - * - * This is a utility function that replaces calls to Texture::setImage(). - * The provided environment map is processed and all mipmap levels are populated. The - * processing is similar to the offline tool `cmgen` as a lower quality setting. - * - * This function is intended to be used when the environment cannot be processed offline, - * for instance if it's generated at runtime. - * - * The source data must obey to some constraints: - * - the data type must be PixelDataFormat::RGB - * - the data format must be one of - * - PixelDataType::FLOAT - * - PixelDataType::HALF - * - * The current texture must be a cubemap - * - * The reflections cubemap's internal format cannot be a compressed format. - * - * The reflections cubemap's dimension must be a power-of-two. + * This non-blocking method checks if the resource has finished creation. If the resource + * creation was initiated asynchronously, it will return true only after all related + * asynchronous tasks are complete. If the resource was created normally without using async + * method, it will always return true. * - * @warning This operation is computationally intensive, especially with large environments and - * is currently synchronous. Expect about 1ms for a 16x16 cubemap. - * - * @param engine Reference to the filament::Engine to associate this IndirectLight with. - * @param buffer Client-side buffer containing the images to set. - * @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets - * are specified in the following order: +x, -x, +y, -y, +z, -z - * @param options Optional parameter to controlling user-specified quality and options. - * - * @exception utils::PreConditionPanic if the source data constraints are not respected. + * @return Whether the resource is created. * + * @see Builder::async() */ - void generatePrefilterMipmap(Engine& engine, - PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, - PrefilterOptions const* UTILS_NULLABLE options = nullptr); - + bool isCreationComplete() const noexcept; /** @deprecated */ struct FaceOffsets { diff --git a/package/android/libs/filament/include/filament/TextureSampler.h b/package/android/libs/filament/include/filament/TextureSampler.h index ba5e5534..ea40a705 100644 --- a/package/android/libs/filament/include/filament/TextureSampler.h +++ b/package/android/libs/filament/include/filament/TextureSampler.h @@ -154,8 +154,8 @@ class UTILS_PUBLIC TextureSampler { /** * This controls anisotropic filtering. - * @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0. - * The maximum permissible value is 7. + * @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 1. + * The maximum permissible value is 128. */ void setAnisotropy(float anisotropy) noexcept { const int log2 = ilogbf(anisotropy > 0 ? anisotropy : -anisotropy); diff --git a/package/android/libs/filament/include/filament/ToneMapper.h b/package/android/libs/filament/include/filament/ToneMapper.h index e89e702c..cff0576e 100644 --- a/package/android/libs/filament/include/filament/ToneMapper.h +++ b/package/android/libs/filament/include/filament/ToneMapper.h @@ -68,6 +68,21 @@ struct UTILS_PUBLIC ToneMapper { * function applied ("linear") */ virtual math::float3 operator()(math::float3 c) const noexcept = 0; + + /** + * If true, then this function holds that f(x) = vec3(f(x.r), f(x.g), f(x.b)) + * + * This may be used to indicate that the color grading's LUT only requires a 1D texture instead + * of a 3D texture, potentially saving a significant amount of memory and generation time. + */ + virtual bool isOneDimensional() const noexcept { return false; } + + /** + * True if this tonemapper only works in low-dynamic-range. + * + * This may be used to indicate that the color grading's LUT doesn't need to be log encoded. + */ + virtual bool isLDR() const noexcept { return false; } }; /** @@ -79,6 +94,8 @@ struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper { ~LinearToneMapper() noexcept final; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return true; } + bool isLDR() const noexcept override { return true; } }; /** @@ -91,6 +108,8 @@ struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper { ~ACESToneMapper() noexcept final; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; /** @@ -104,6 +123,8 @@ struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper { ~ACESLegacyToneMapper() noexcept final; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; /** @@ -117,6 +138,8 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper { ~FilmicToneMapper() noexcept final; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return true; } + bool isLDR() const noexcept override { return false; } }; /** @@ -129,6 +152,8 @@ struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper { ~PBRNeutralToneMapper() noexcept final; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; /** @@ -150,6 +175,8 @@ struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper { ~AgxToneMapper() noexcept final; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } AgxLook look; }; @@ -194,6 +221,8 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper { GenericToneMapper& operator=(GenericToneMapper&& rhs) noexcept; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return true; } + bool isLDR() const noexcept override { return false; } /** Returns the contrast of the curve as a strictly positive value. */ float getContrast() const noexcept; @@ -256,6 +285,8 @@ struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper { ~DisplayRangeToneMapper() noexcept override; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; } // namespace filament diff --git a/package/android/libs/filament/include/filament/VertexBuffer.h b/package/android/libs/filament/include/filament/VertexBuffer.h index fccbd004..b29c7f9a 100644 --- a/package/android/libs/filament/include/filament/VertexBuffer.h +++ b/package/android/libs/filament/include/filament/VertexBuffer.h @@ -26,7 +26,9 @@ #include #include +#include +#include #include #include @@ -60,8 +62,12 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { public: using AttributeType = backend::ElementType; using BufferDescriptor = backend::BufferDescriptor; + using AsyncCompletionCallback = + std::function; + using AsyncCallId = backend::AsyncCallId; - class Builder : public BuilderBase { + + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -158,6 +164,56 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { */ Builder& advancedSkinning(bool enabled) noexcept; + /** + * Associate an optional name with this VertexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this VertexBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this VertexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this VertexBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + + /** + * Specifies a callback that will execute once the resource's data has been fully allocated + * within the GPU memory. This enables the resource creation process to be handled + * asynchronously. + * + * Any asynchronous calls made during a resource's asynchronous creation (using this method) + * are safe because they are queued and executed in sequence. However, invoking regular + * methods on the same resource before it's fully ready is unsafe and may cause undefined + * behavior. Users can call the `isCreationComplete()` method for the resource to confirm + * when the resource is ready for regular API calls. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * @return This Builder, for chaining calls. + */ + Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback = nullptr, + void* UTILS_NULLABLE user = nullptr) noexcept; + /** * Creates the VertexBuffer object and returns a pointer to it. * @@ -182,7 +238,7 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { size_t getVertexCount() const noexcept; /** - * Asynchronously copy-initializes the specified buffer from the given buffer data. + * copy-initializes the specified buffer from the given buffer data. * * Do not use this if you called enableBufferObjects() on the Builder. * @@ -193,11 +249,41 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { * index \p bufferIndex. BufferDescriptor points to raw, untyped data that will * be copied as-is into the buffer. * @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex - * buffer set. + * buffer set. Must be multiple of 4. */ void setBufferAt(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + /** + * An asynchronous version of `setBufferAt()`. + * Asynchronously copy-initializes the specified buffer from the given buffer data. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * Do not use this if you called enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param buffer A BufferDescriptor representing the data used to initialize the buffer at + * index \p bufferIndex. BufferDescriptor points to raw, untyped data that will + * be copied as-is into the buffer. + * @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex + * buffer set. Must be multiple of 4. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + */ + AsyncCallId setBufferAtAsync(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer, + uint32_t byteOffset, backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, void* UTILS_NULLABLE user = nullptr); + /** * Swaps in the given buffer object. * @@ -211,6 +297,45 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* UTILS_NONNULL bufferObject); + /** + * An asynchronous version of `setBufferObjectAt()`. + * Swaps in the given buffer object. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * To use this, you must first call enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param bufferObject The handle to the GPU data that will be used in this buffer slot. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + */ + AsyncCallId setBufferObjectAtAsync(Engine& engine, uint8_t bufferIndex, + BufferObject const* UTILS_NONNULL bufferObject, + backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, void* UTILS_NULLABLE user = nullptr); + + /** + * This non-blocking method checks if the resource has finished creation. If the resource + * creation was initiated asynchronously, it will return true only after all related + * asynchronous tasks are complete. If the resource was created normally without using async + * method, it will always return true. + * + * @return Whether the resource is created. + * + * @see Builder::async() + */ + bool isCreationComplete() const noexcept; + protected: // prevent heap allocation ~VertexBuffer() = default; diff --git a/package/android/libs/filament/include/filament/View.h b/package/android/libs/filament/include/filament/View.h index 3cdd527f..0240bafa 100644 --- a/package/android/libs/filament/include/filament/View.h +++ b/package/android/libs/filament/include/filament/View.h @@ -24,8 +24,10 @@ #include #include +#include #include +#include #include @@ -40,6 +42,7 @@ class CallbackHandler; class Camera; class ColorGrading; +class Engine; class MaterialInstance; class RenderTarget; class Scene; @@ -182,27 +185,55 @@ class UTILS_PUBLIC View : public FilamentAPI { * View.\n * The View doesn't take ownership of the Camera pointer (which * acts as a reference). + * If the camera isn't set, Renderer::render() will result in a no-op. * * @note * There is no reference-counting. * Make sure to dissociate a Camera from all Views before destroying it. */ - void setCamera(Camera* UTILS_NONNULL camera) noexcept; + void setCamera(Camera* UTILS_NULLABLE camera) noexcept; + + /** + * Returns whether a Camera is set. + * @return true if a camera is set. + * @see setCamera() + */ + bool hasCamera() const noexcept; /** * Returns the Camera currently associated with this View. - * @return A reference to the Camera associated to this View. + * Undefined behavior if hasCamera() is false. + * @return A reference to the Camera associated to this View if hasCamera() is true. + * @see hasCamera() */ Camera& getCamera() noexcept; /** * Returns the Camera currently associated with this View. + * Undefined behavior if hasCamera() is false. * @return A reference to the Camera associated to this View. + * @see hasCamera() */ Camera const& getCamera() const noexcept { return const_cast(this)->getCamera(); } + + /** + * Sets whether a channel must clear the depth buffer before all primitives are rendered. + * Channel depth clear is off by default for all channels. + * This is orthogonal to Renderer::setClearOptions(). + * @param channel between 0 and 7 + * @param enabled true to enable clear, false to disable + */ + void setChannelDepthClearEnabled(uint8_t channel, bool enabled) noexcept; + + /** + * @param channel between 0 and 7 + * @return true if this channel has depth clear enabled. + */ + bool isChannelDepthClearEnabled(uint8_t channel) const noexcept; + /** * Sets the blending mode used to draw the view into the SwapChain. * @@ -510,6 +541,14 @@ class UTILS_PUBLIC View : public FilamentAPI { */ DynamicResolutionOptions getDynamicResolutionOptions() const noexcept; + /** + * Returns the last dynamic resolution scale factor used by this view. This value is updated + * when Renderer::render(View*) is called + * @return a float2 where x is the horizontal and y the vertical scale factor. + * @see Renderer::render + */ + math::float2 getLastDynamicResolutionScale() const noexcept; + /** * Sets the rendering quality for this view. Refer to RenderQuality for more * information about the different settings available. @@ -533,12 +572,13 @@ class UTILS_PUBLIC View : public FilamentAPI { * visible -- in this case, using a larger value can improve performance. * e.g. when standing and looking straight, several meters of the ground * isn't visible and if lights are expected to shine there, there is no - * point using a short zLightNear. (Default 5m). + * point using a short zLightNear. This value is clamped between + * the camera near and far plane. (Default 5m). * * @param zLightFar Distance from the camera after which lights are not expected to be visible. * Similarly to zLightNear, setting this value properly can improve - * performance. (Default 100m). - * + * performance. This value is clamped between the camera near and far plane. + * (Default 100m). * * Together zLightNear and zLightFar must be chosen so that the visible influence of lights * is spread between these two values. @@ -561,6 +601,13 @@ class UTILS_PUBLIC View : public FilamentAPI { */ void setShadowType(ShadowType shadow) noexcept; + /** + * Returns the shadow mapping technique used by this View. + * + * @return value set by setShadowType(). + */ + ShadowType getShadowType() const noexcept; + /** * Sets VSM shadowing options that apply across the entire View. * @@ -652,6 +699,26 @@ class UTILS_PUBLIC View : public FilamentAPI { */ bool isFrontFaceWindingInverted() const noexcept; + /** + * Enables or disables transparent picking. Disabled by default. + * + * When transparent picking is enabled, View::pick() will pick from both + * transparent and opaque renderables. When disabled, View::pick() will only + * pick from opaque renderables. + * + * @param enabled true enables transparent picking, false disables it. + * + * @note Transparent picking will create an extra pass for rendering depth + * from both transparent and opaque renderables. + */ + void setTransparentPickingEnabled(bool enabled) noexcept; + + /** + * Returns true if transparent picking is enabled. + * See setTransparentPickingEnabled() for more information. + */ + bool isTransparentPickingEnabled() const noexcept; + /** * Enables use of the stencil buffer. * @@ -719,8 +786,31 @@ class UTILS_PUBLIC View : public FilamentAPI { void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept; //! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing. - Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept; + utils::FixedCapacityVector getDirectionalShadowCameras() const noexcept; + + //! debugging: enable or disable froxel visualisation for this view. + void setFroxelVizEnabled(bool enabled) noexcept; + + //! debugging: returns information about the froxel configuration + struct FroxelConfigurationInfo { + uint16_t width; + uint16_t height; + uint16_t depth; + uint32_t viewportWidth; + uint32_t viewportHeight; + math::uint2 froxelDimension; + float zLightFar; + float linearizer; + math::mat4f p; + math::float4 clipTransform; + }; + + struct FroxelConfigurationInfoWithAge { + FroxelConfigurationInfo info; + uint32_t age; + }; + FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept; /** Result of a picking query */ struct PickingQueryResult { @@ -871,6 +961,17 @@ class UTILS_PUBLIC View : public FilamentAPI { */ utils::Entity getFogEntity() const noexcept; + + /** + * When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view + * keeps a history of previous frame renders associated with the Renderer the view was last + * used with. When switching Renderer, it may be necessary to clear that history by calling + * this method. Similarly, if the whole content of the screen change, like when a cut-scene + * starts, clearing the history might be needed to avoid artifacts due to the previous frame + * being very different. + */ + void clearFrameHistory(Engine& engine) noexcept; + /** * List of available ambient occlusion techniques * @deprecated use AmbientOcclusionOptions::enabled instead diff --git a/package/android/libs/filament/include/geometry/TangentSpaceMesh.h b/package/android/libs/filament/include/geometry/TangentSpaceMesh.h index 980e81ac..1872aaad 100644 --- a/package/android/libs/filament/include/geometry/TangentSpaceMesh.h +++ b/package/android/libs/filament/include/geometry/TangentSpaceMesh.h @@ -340,7 +340,7 @@ class TangentSpaceMesh { std::is_same::value || std::is_same::value>::type; template> - void getAux(AuxAttribute attribute, T* out, size_t stride = 0) const noexcept; + void getAux(AuxAttribute attribute, T* out, size_t stride = 0) const; /** * Get number of output triangles. diff --git a/package/android/libs/filament/include/gltfio/AssetLoader.h b/package/android/libs/filament/include/gltfio/AssetLoader.h index f516166a..bf650f69 100644 --- a/package/android/libs/filament/include/gltfio/AssetLoader.h +++ b/package/android/libs/filament/include/gltfio/AssetLoader.h @@ -38,6 +38,23 @@ namespace filament::gltfio { class NodeManager; +// Use this struct to enable mikktspace-based tangent-space computation. +/** + * \struct AssetConfigurationExtended AssetLoader.h gltfio/AssetLoader.h + * \brief extends struct AssetConfiguration + * Useful if client needs mikktspace tangent space computation. + * NOTE: Android, iOS, Web are not supported. And only disk-local glTF resources are supported. + */ +struct AssetConfigurationExtended { + //! Optional The same parameter as provided to \struct ResourceConfiguration ResourceLoader.h + //! gltfio/ResourceLoader.h + char const* gltfPath; + + //! Client can use this method to check if the extended implementation is supported on their + //! platform or not. + static bool isSupported(); +}; + /** * \struct AssetConfiguration AssetLoader.h gltfio/AssetLoader.h * \brief Construction parameters for AssetLoader. @@ -62,6 +79,10 @@ struct AssetConfiguration { //! Optional default node name for anonymous nodes char* defaultNodeName = nullptr; + + //! Optional to enable mikktspace tangents. Lifetime of struct only needs to be maintained for + // the duration of the constructor of AssetLoader. + AssetConfigurationExtended* ext = nullptr; }; /** diff --git a/package/android/libs/filament/include/gltfio/MaterialProvider.h b/package/android/libs/filament/include/gltfio/MaterialProvider.h index f62d667b..e4cf01b3 100644 --- a/package/android/libs/filament/include/gltfio/MaterialProvider.h +++ b/package/android/libs/filament/include/gltfio/MaterialProvider.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -93,10 +94,18 @@ struct alignas(4) MaterialKey { bool hasSheen : 1; bool hasIOR : 1; bool hasVolume : 1; - uint8_t padding : 5; + bool hasDispersion : 1; + bool hasSpecular : 1; + bool hasSpecularTexture : 1; + bool hasSpecularColorTexture : 1; + bool padding : 1; + // -- 32 bit boundary -- + uint8_t specularTextureUV; + uint8_t specularColorTextureUV; + uint16_t padding2; }; -static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size."); +static_assert(sizeof(MaterialKey) == 20, "MaterialKey has unexpected size."); UTILS_WARNING_POP @@ -189,6 +198,7 @@ void processShaderString(std::string* shader, const UvMap& uvmap, * Creates a material provider that builds materials on the fly, composing GLSL at run time. * * @param optimizeShaders Optimizes shaders, but at significant cost to construction time. + * @param variantFilters Filter out variants that are not required. * @return New material provider that can build materials at run time. * * Requires \c libfilamat to be linked in. Not available in \c libgltfio_core. @@ -196,7 +206,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, * @see createUbershaderProvider */ UTILS_PUBLIC -MaterialProvider* createJitShaderProvider(Engine* engine, bool optimizeShaders = false); +MaterialProvider* createJitShaderProvider(Engine* engine, bool optimizeShaders = false, + utils::FixedCapacityVector const& variantFilters = {}); /** * Creates a material provider that loads a small set of pre-built materials. diff --git a/package/android/libs/filament/include/gltfio/TextureProvider.h b/package/android/libs/filament/include/gltfio/TextureProvider.h index ff497af2..0015c86a 100644 --- a/package/android/libs/filament/include/gltfio/TextureProvider.h +++ b/package/android/libs/filament/include/gltfio/TextureProvider.h @@ -179,6 +179,19 @@ TextureProvider* createStbProvider(filament::Engine* engine); */ TextureProvider* createKtx2Provider(filament::Engine* engine); +/** + * If webp support is enabled at build time, creates a decoder that can handle "image/webp" + * lossless and lossy content. + * If webp support is not enabled at build time, returns nullptr. + */ +TextureProvider* createWebpProvider(filament::Engine* engine); + +/** + * Indicates if build-time webp support was included. + * Returns true if it was and false if not. + */ +bool isWebpSupported(); + } // namespace filament::gltfio template<> struct utils::EnableBitMaskOperators diff --git a/package/android/libs/filament/include/gltfio/materials/uberarchive.h b/package/android/libs/filament/include/gltfio/materials/uberarchive.h index b36ddd36..ca85fa42 100644 --- a/package/android/libs/filament/include/gltfio/materials/uberarchive.h +++ b/package/android/libs/filament/include/gltfio/materials/uberarchive.h @@ -5,9 +5,10 @@ extern "C" { extern const uint8_t UBERARCHIVE_PACKAGE[]; - extern int UBERARCHIVE_DEFAULT_OFFSET; - extern int UBERARCHIVE_DEFAULT_SIZE; } + +#define UBERARCHIVE_DEFAULT_OFFSET 0 +#define UBERARCHIVE_DEFAULT_SIZE 1080249 #define UBERARCHIVE_DEFAULT_DATA (UBERARCHIVE_PACKAGE + UBERARCHIVE_DEFAULT_OFFSET) #endif diff --git a/package/android/libs/filament/include/ibl/CubemapIBL.h b/package/android/libs/filament/include/ibl/CubemapIBL.h index 925e27c0..ae8fcb08 100644 --- a/package/android/libs/filament/include/ibl/CubemapIBL.h +++ b/package/android/libs/filament/include/ibl/CubemapIBL.h @@ -54,7 +54,7 @@ class UTILS_PUBLIC CubemapIBL { * @param updater a callback for the caller to track progress */ static void roughnessFilter( - utils::JobSystem& js, Cubemap& dst, const utils::Slice& levels, + utils::JobSystem& js, Cubemap& dst, utils::Slice levels, float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter, Progress updater = nullptr, void* userdata = nullptr); diff --git a/package/android/libs/filament/include/math/TQuatHelpers.h b/package/android/libs/filament/include/math/TQuatHelpers.h index 0439b971..7ff641b0 100644 --- a/package/android/libs/filament/include/math/TQuatHelpers.h +++ b/package/android/libs/filament/include/math/TQuatHelpers.h @@ -259,10 +259,11 @@ class TQuatFunctions { return normalize(lerp(d < 0 ? -p : p, q, t)); } const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| - const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1))); + const T cos_a = math::clamp(absd / npq, T(-1), T(1)); + const T a = std::acos(cos_a); const T a0 = a * (1 - t); const T a1 = a * t; - const T sina = sin(a); + const T sina = std::sqrt(T(1) - cos_a * cos_a); if (sina < value_eps) { return normalize(lerp(p, q, t)); } diff --git a/package/android/libs/filament/include/math/compiler.h b/package/android/libs/filament/include/math/compiler.h index 634e2077..a7d85168 100644 --- a/package/android/libs/filament/include/math/compiler.h +++ b/package/android/libs/filament/include/math/compiler.h @@ -122,6 +122,11 @@ struct is_arithmetic : std::integral_constant::value || std::is_floating_point::value> { }; +template +struct is_floating_point : std::integral_constant::value> { +}; + } // filament::math #endif // TNT_MATH_COMPILER_H diff --git a/package/android/libs/filament/include/math/fast.h b/package/android/libs/filament/include/math/fast.h index 85b990d2..9988cfd2 100644 --- a/package/android/libs/filament/include/math/fast.h +++ b/package/android/libs/filament/include/math/fast.h @@ -50,7 +50,7 @@ constexpr T MATH_PURE cos(T x) noexcept { // x between -pi and pi template::value>> constexpr T MATH_PURE sin(T x) noexcept { - return filament::math::fast::cos(x - T(F_PI_2)); + return fast::cos(x - T(F_PI_2)); } constexpr inline float MATH_PURE ilog2(float x) noexcept { diff --git a/package/android/libs/filament/include/math/half.h b/package/android/libs/filament/include/math/half.h index d792e1bf..4578708b 100644 --- a/package/android/libs/filament/include/math/half.h +++ b/package/android/libs/filament/include/math/half.h @@ -164,20 +164,20 @@ inline constexpr half operator""_h(long double v) { return half( static_cast(v) ); } -template<> struct is_arithmetic : public std::true_type {}; +template<> struct is_arithmetic : public std::true_type {}; + +template<> struct is_floating_point : public std::true_type {}; } // namespace math } // namespace filament namespace std { -template<> struct is_floating_point : public std::true_type {}; - -// note: this shouldn't be needed (is_floating_point<> is enough) but some version of msvc need it -// This stopped working with MSVC 2019 16.4, so we specialize our own version of is_arithmetic in +// Remove the standard template specializations for filament::math::half +// Clang 20 explicitly blocks customizing standard type traits +// Instead, use the traits defined in the math:: namespace +// This avoids compatibility issues with Clang 20 and MSVC 2019 16.4+ // the math::filament namespace (see above). -template<> struct is_arithmetic : public std::true_type {}; - template<> class numeric_limits { public: diff --git a/package/android/libs/filament/include/math/mat2.h b/package/android/libs/filament/include/math/mat2.h index dba9ca47..d12aa532 100644 --- a/package/android/libs/filament/include/math/mat2.h +++ b/package/android/libs/filament/include/math/mat2.h @@ -225,7 +225,7 @@ class MATH_EMPTY_BASES TMat22 : * Rotate by radians in the 2D plane */ static TMat22 rotate(T radian) noexcept { - TMat22 r(TMat22::NO_INIT); + TMat22 r(NO_INIT); T c = std::cos(radian); T s = std::sin(radian); r[0][0] = c; diff --git a/package/android/libs/filament/include/math/mat3.h b/package/android/libs/filament/include/math/mat3.h index 035865fe..57ad7ec3 100644 --- a/package/android/libs/filament/include/math/mat3.h +++ b/package/android/libs/filament/include/math/mat3.h @@ -256,7 +256,7 @@ class MATH_EMPTY_BASES TMat33 : */ friend inline constexpr TMat33 orthogonalize(const TMat33& m) noexcept { - TMat33 ret(TMat33::NO_INIT); + TMat33 ret(NO_INIT); ret[0] = normalize(m[0]); ret[2] = normalize(cross(ret[0], m[1])); ret[1] = normalize(cross(ret[2], ret[0])); @@ -479,7 +479,7 @@ constexpr TQuaternion TMat33::packTangentFrame(const TMat33& m, size_t template constexpr details::TMat33 prescaleForNormals(const details::TMat33& m) noexcept { return m * details::TMat33( - 1.0 / std::sqrt(max(float3{length2(m[0]), length2(m[1]), length2(m[2])}))); + T(1.0) / std::sqrt(max(float3{length2(m[0]), length2(m[1]), length2(m[2])}))); } // ---------------------------------------------------------------------------------------- diff --git a/package/android/libs/filament/include/math/mat4.h b/package/android/libs/filament/include/math/mat4.h index 57058539..7eb50e35 100644 --- a/package/android/libs/filament/include/math/mat4.h +++ b/package/android/libs/filament/include/math/mat4.h @@ -498,10 +498,10 @@ constexpr TMat44 TMat44::frustum(T left, T right, T bottom, T top, T near, } template -TMat44 TMat44::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) noexcept { +TMat44 TMat44::perspective(T fov, T aspect, T near, T far, Fov direction) noexcept { T h, w; - if (direction == TMat44::Fov::VERTICAL) { + if (direction == Fov::VERTICAL) { h = std::tan(fov * F_PI / 360.0f) * near; w = h * aspect; } else { diff --git a/package/android/libs/filament/include/private/backend/VirtualMachineEnv.h b/package/android/libs/filament/include/private/backend/VirtualMachineEnv.h new file mode 100644 index 00000000..c4352391 --- /dev/null +++ b/package/android/libs/filament/include/private/backend/VirtualMachineEnv.h @@ -0,0 +1,64 @@ +/* + * 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_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H +#define TNT_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H + +#include +#include + +#include + +namespace filament { + +class VirtualMachineEnv { +public: + // must be called before VirtualMachineEnv::get() from a thread that is attached to the JavaVM + static jint JNI_OnLoad(JavaVM* vm); + + // must be called on backend thread + static VirtualMachineEnv& get() noexcept; + + // can be called from any thread that already has a JniEnv + static JNIEnv* getThreadEnvironment(); + + // must be called from the backend thread + JNIEnv* getEnvironment() noexcept { + JNIEnv* env = mJniEnv; + if (UTILS_UNLIKELY(!env)) { + return getEnvironmentSlow(); + } + return env; + } + + static void handleException(JNIEnv* env) noexcept; + +private: + explicit VirtualMachineEnv(JavaVM* vm) noexcept; + ~VirtualMachineEnv() noexcept; + JNIEnv* getEnvironmentSlow(); + + static utils::Mutex sLock; + static JavaVM* sVirtualMachine; + static JavaVM* getVirtualMachine(); + + JNIEnv* mJniEnv = nullptr; + JavaVM* mVirtualMachine = nullptr; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H diff --git a/package/android/libs/filament/include/uberz/WritableArchive.h b/package/android/libs/filament/include/uberz/WritableArchive.h index e511d95f..b383b70c 100644 --- a/package/android/libs/filament/include/uberz/WritableArchive.h +++ b/package/android/libs/filament/include/uberz/WritableArchive.h @@ -56,7 +56,7 @@ class WritableArchive { utils::FixedCapacityVector package; Shading shadingModel; BlendingMode blendingMode; - tsl::robin_map flags; + tsl::robin_map flags; }; utils::FixedCapacityVector mMaterials; diff --git a/package/android/libs/filament/include/utils/Allocator.h b/package/android/libs/filament/include/utils/Allocator.h index 7b9978f5..d74b8cc8 100644 --- a/package/android/libs/filament/include/utils/Allocator.h +++ b/package/android/libs/filament/include/utils/Allocator.h @@ -18,7 +18,6 @@ #define TNT_UTILS_ALLOCATOR_H #include -#include #include #include @@ -36,22 +35,22 @@ namespace utils { namespace pointermath { -template -static inline P* add(P* a, T b) noexcept { - return (P*)(uintptr_t(a) + uintptr_t(b)); +template +static P* add(P* a, T b) noexcept { + return (P*) (uintptr_t(a) + uintptr_t(b)); } -template -static inline P* align(P* p, size_t alignment) noexcept { +template +static P* align(P* p, size_t alignment) noexcept { // alignment must be a power-of-two - assert_invariant(alignment && !(alignment & alignment-1)); - return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1)); + assert(alignment && !(alignment & alignment-1)); + return (P*) ((uintptr_t(p) + alignment - 1) & ~(alignment - 1)); } -template -static inline P* align(P* p, size_t alignment, size_t offset) noexcept { +template +static P* align(P* p, size_t alignment, size_t offset) noexcept { P* const r = align(add(p, offset), alignment); - assert_invariant(r >= add(p, offset)); + assert(r >= add(p, offset)); return r; } @@ -102,7 +101,7 @@ class LinearAllocator { // free memory back to the specified point void rewind(void* p) UTILS_RESTRICT noexcept { - assert_invariant(p >= mBegin && p < end()); + assert(p >= mBegin && p < end()); set_current(p); } @@ -194,7 +193,7 @@ class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocat } ~LinearAllocatorWithFallback() noexcept { - LinearAllocatorWithFallback::reset(); + reset(); } void* alloc(size_t size, size_t alignment = alignof(std::max_align_t)); @@ -204,7 +203,7 @@ class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocat } void rewind(void* p) noexcept { - if (p >= LinearAllocator::base() && p < LinearAllocator::end()) { + if (p >= base() && p < end()) { LinearAllocator::rewind(p); } } @@ -214,7 +213,7 @@ class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocat void free(void*, size_t) noexcept { } bool isHeapAllocation(void* p) const noexcept { - return p < LinearAllocator::base() || p >= LinearAllocator::end(); + return p < base() || p >= end(); } }; @@ -233,15 +232,16 @@ class FreeList { Node* const head = mHead; mHead = head ? head->next : nullptr; // this could indicate a use after free - assert_invariant(!mHead || mHead >= mBegin && mHead < mEnd); + assert(!mHead || mHead >= mBegin && mHead < mEnd); return head; } void push(void* p) noexcept { - assert_invariant(p); - assert_invariant(p >= mBegin && p < mEnd); - // TODO: assert this is one of our pointer (i.e.: it's address match one of ours) - Node* const head = static_cast(p); + assert(p); + assert(p >= mBegin && p < mEnd); + // we use placement-new to properly manage the lifetime + // this is noop already under O1 + Node* const head = new (p) Node; head->next = mHead; mHead = head; } @@ -278,41 +278,50 @@ class AtomicFreeList { void* pop() noexcept { Node* const pStorage = mStorage; - HeadPtr currentHead = mHead.load(); + HeadPtr currentHead = mHead.load(std::memory_order_relaxed); while (currentHead.offset >= 0) { // The value of "pNext" we load here might already contain application data if another // thread raced ahead of us. But in that case, the computed "newHead" will be discarded - // since compare_exchange_weak fails. Then this thread will loop with the updated + // since compare_exchange_weak() fails. Then this thread will loop with the updated // value of currentHead, and try again. - Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed); + // TSAN complains if we don't use a local variable here. + Node const node = pStorage[currentHead.offset]; + Node const* const pNext = node.next; const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 }; - // In the rare case that the other thread that raced ahead of us already returned the - // same mHead we just loaded, but it now has a different "next" value, the tag field will not - // match, and compare_exchange_weak will fail and prevent that particular race condition. - if (mHead.compare_exchange_weak(currentHead, newHead)) { + // In the rare case that the other thread that raced ahead of us already returned the + // same mHead we just loaded, but it now has a different "next" value, the tag field + // will not match, and compare_exchange_weak() will fail and prevent that particular + // race condition. + // acquire: no read/write can be reordered before this + if (mHead.compare_exchange_weak(currentHead, newHead, + std::memory_order_acquire, std::memory_order_relaxed)) { // This assert needs to occur after we have validated that there was no race condition // Otherwise, next might already contain application data, if another thread // raced ahead of us after we loaded mHead, but before we loaded mHead->next. - assert_invariant(!pNext || pNext >= pStorage); + assert(!pNext || pNext >= pStorage); break; } } void* p = (currentHead.offset >= 0) ? (pStorage + currentHead.offset) : nullptr; - assert_invariant(!p || p >= pStorage); + assert(!p || p >= pStorage); return p; } void push(void* p) noexcept { Node* const storage = mStorage; - assert_invariant(p && p >= storage); - Node* const node = static_cast(p); - HeadPtr currentHead = mHead.load(); + assert(p && p >= storage); + // we use placement-new to properly manage the lifetime + // this is noop already under O1 + Node* const node = new (p) Node; + HeadPtr currentHead = mHead.load(std::memory_order_relaxed); HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 }; do { newHead.tag = currentHead.tag + 1; - Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; - node->next.store(n, std::memory_order_relaxed); - } while(!mHead.compare_exchange_weak(currentHead, newHead)); + Node* const pNext = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; + node->next = pNext; // could be a race with pop, corrected by CAS + } while(!mHead.compare_exchange_weak(currentHead, newHead, + std::memory_order_release, std::memory_order_relaxed)); + // release: no read/write can be reordered after this } void* getFirst() noexcept { @@ -320,10 +329,7 @@ class AtomicFreeList { } struct Node { - // This should be a regular (non-atomic) pointer, but this causes TSAN to complain - // about a data-race that exists but is benin. We always use this atomic<> in - // relaxed mode. - // The data race TSAN complains about is when a pop() is interrupted by a + // There is a benign data race when a pop() is interrupted by a // pop() + push() just after mHead->next is read -- it appears as though it is written // without synchronization (by the push), however in that case, the pop's CAS will fail // and things will auto-correct. @@ -346,7 +352,7 @@ class AtomicFreeList { // | // CAS, tag++ // - std::atomic next; + Node* next = nullptr; }; private: @@ -378,9 +384,9 @@ class PoolAllocator { // our allocator concept void* alloc(size_t size = ELEMENT_SIZE, size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept { - assert_invariant(size <= ELEMENT_SIZE); - assert_invariant(alignment <= ALIGNMENT); - assert_invariant(offset == OFFSET); + assert(size <= ELEMENT_SIZE); + assert(alignment <= ALIGNMENT); + assert(offset == OFFSET); return mFreeList.pop(); } @@ -458,7 +464,7 @@ class PoolAllocatorWithFallback : if (UTILS_UNLIKELY(!p)) { p = HeapAllocator::alloc(size, alignment); } - assert_invariant(p); + assert(p); return p; } @@ -714,13 +720,13 @@ class Arena { // trivially destructible, since free() won't call the destructor and this is allocating // an array. template ::value>::type> + typename = std::enable_if_t>> T* alloc(size_t count, size_t alignment, size_t extra) noexcept { return (T*)alloc(count * sizeof(T), alignment, extra); } template ::value>::type> + typename = std::enable_if_t>> T* alloc(size_t count, size_t alignment = alignof(T)) noexcept { return (T*)alloc(count * sizeof(T), alignment); } @@ -876,7 +882,7 @@ class ArenaScope { template T* make(ARGS&& ... args) noexcept { T* o = nullptr; - if (std::is_trivially_destructible::value) { + if (std::is_trivially_destructible_v) { o = mArena.template make(std::forward(args)...); } else { void* const p = (Finalizer*)mArena.alloc(sizeof(T), ALIGN, sizeof(Finalizer)); @@ -939,7 +945,7 @@ class STLAllocator { TYPE* allocate(std::size_t n) { auto p = static_cast(mArena.alloc(n * sizeof(TYPE), alignof(TYPE))); - assert_invariant(p); + assert(p); return p; } diff --git a/package/android/libs/filament/include/utils/BitmaskEnum.h b/package/android/libs/filament/include/utils/BitmaskEnum.h index 17f94d21..ae412204 100644 --- a/package/android/libs/filament/include/utils/BitmaskEnum.h +++ b/package/android/libs/filament/include/utils/BitmaskEnum.h @@ -41,32 +41,32 @@ size_t count(); // ------------------------------------------------------------------------------------------------ template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr int operator+(Enum value) noexcept { return int(value); } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator==(Enum lhs, size_t rhs) noexcept { using underlying_t = std::underlying_type_t; return underlying_t(lhs) == rhs; } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator==(size_t lhs, Enum rhs) noexcept { return rhs == lhs; } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator!=(Enum lhs, size_t rhs) noexcept { return !(rhs == lhs); } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator!=(size_t lhs, Enum rhs) noexcept { return rhs != lhs; } @@ -74,66 +74,66 @@ inline constexpr bool operator!=(size_t lhs, Enum rhs) noexcept { // ------------------------------------------------------------------------------------------------ template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr bool operator!(Enum rhs) noexcept { using underlying = std::underlying_type_t; return underlying(rhs) == 0; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator~(Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(~underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(underlying(lhs) | underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(underlying(lhs) & underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(underlying(lhs) ^ underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs | rhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs & rhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs ^ rhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr bool none(Enum lhs) noexcept { return !lhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr bool any(Enum lhs) noexcept { return !none(lhs); } diff --git a/package/android/libs/filament/include/utils/CString.h b/package/android/libs/filament/include/utils/CString.h index d5b76951..935845f2 100644 --- a/package/android/libs/filament/include/utils/CString.h +++ b/package/android/libs/filament/include/utils/CString.h @@ -20,6 +20,11 @@ // NOTE: this header should not include STL headers #include +#include + +#include +#include +#include #include #include @@ -28,27 +33,22 @@ #include 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 const c = *cstr++) { - hash = (hash * 33u) ^ size_t(c); - } - return hash; - } -}; +namespace io { +class ostream; +} template using StringLiteral = const char[N]; - // ------------------------------------------------------------------------------------------------ class UTILS_PUBLIC CString { + static constexpr bool TRACK_AND_LOG_ALLOCATIONS = false; + + template + static constexpr bool is_char_pointer_v = + std::is_pointer_v && std::is_same_v>>; + public: using value_type = char; using size_type = uint32_t; @@ -60,7 +60,9 @@ class UTILS_PUBLIC CString { using iterator = value_type*; using const_iterator = const value_type*; - CString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug + CString() noexcept { + track(true); + } // 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). @@ -71,22 +73,45 @@ class UTILS_PUBLIC CString { // inside the string. explicit CString(size_t length); + // Conversion from std::string_view + explicit CString(const std::string_view& str) + : CString(str.data(), str.size()) { + } + // Allocates memory and copies traditional C string content. Unlike the above constructor, this // does not allow embedded nulls. This is explicit because this operation is costly. - explicit CString(const char* cstr); + // This is a template to ensure it's not preferred over the string literal constructor below. + template>> + explicit CString(T cstr) : CString(cstr, cstr ? strlen(cstr) : 0) { + track(true); + } + // The string can't have NULs in it. template CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) : CString(other, N - 1) { + track(true); + } + + // This constructor can be used if the string has NULs in it. + template + CString(StringLiteral const& other, size_t const length) noexcept + : CString(other, length) { + track(true); + } + + CString(StaticString const& other) noexcept // NOLINT(*-explicit-constructor) + : CString(other.c_str(), other.length()) { + track(true); } CString(const CString& rhs); CString(CString&& rhs) noexcept { + track(true); this->swap(rhs); } - CString& operator=(const CString& rhs); CString& operator=(CString&& rhs) noexcept { @@ -94,11 +119,7 @@ class UTILS_PUBLIC CString { return *this; } - ~CString() noexcept { - if (mData) { - free(mData - 1); - } - } + ~CString() noexcept; void swap(CString& other) noexcept { // don't use std::swap(), we don't want an STL dependency in this file @@ -107,8 +128,8 @@ class UTILS_PUBLIC CString { other.mCStr = temp; } - const_pointer c_str() const noexcept { return mCStr; } pointer c_str() noexcept { return mCStr; } + const_pointer c_str() const noexcept { return const_cast(this)->c_str(); } 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(); } @@ -116,32 +137,169 @@ class UTILS_PUBLIC CString { size_type length() const noexcept { return size(); } bool empty() const noexcept { return size() == 0; } - iterator begin() noexcept { return mCStr; } + iterator begin() noexcept { return c_str(); } 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); } + // replace + template + CString& replace(size_type const pos, size_type const len, const StringLiteral& str) & noexcept { + return replace(pos, len, str, N - 1); + } + + CString& replace(size_type const pos, size_type const len, const CString& str) & noexcept { + return replace(pos, len, str.c_str_safe(), str.size()); + } + + template >> + CString& replace(size_type pos, size_type len, T str) & noexcept { + if (str) { + return replace(pos, len, str, strlen(str)); + } + return replace(pos, len, "", 0); + } + + template + CString&& replace(size_type pos, size_type len, const StringLiteral& str) && noexcept { + this->replace(pos, len, str); + return std::move(*this); + } + + CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept { + this->replace(pos, len, str); + return std::move(*this); + } + + template >> + CString&& replace(size_type pos, size_type len, T str) && noexcept { + this->replace(pos, len, str); + return std::move(*this); + } + + + // insert + CString& insert(size_type const pos, char const c) & noexcept { + const char s[1] = { c }; + return replace(pos, 0, s, 1); + } + + template + CString& insert(size_type const pos, const StringLiteral& str) & noexcept { + return replace(pos, 0, str, N - 1); + } + + CString& insert(size_type const pos, const CString& str) & noexcept { + return replace(pos, 0, str.c_str_safe(), str.size()); + } + + template >> + CString& insert(size_type pos, T str) & noexcept { + if (str) { + return replace(pos, 0, str, strlen(str)); + } + return *this; + } + + CString&& insert(size_type const pos, char const c) && noexcept { + this->insert(pos, c); + return std::move(*this); + } + + template + CString&& insert(size_type pos, const StringLiteral& str) && noexcept { + this->insert(pos, str); + return std::move(*this); + } + + CString&& insert(size_type const pos, const CString& str) && noexcept { + this->insert(pos, str); + return std::move(*this); + } + + template >> + CString&& insert(size_type pos, T str) && noexcept { + this->insert(pos, str); + return std::move(*this); + } + + + // append + CString& append(char const c) & noexcept { + return insert(length(), c); + } + + template + CString& append(const StringLiteral& str) & noexcept { + return insert(length(), str); + } + + CString& append(const CString& str) & noexcept { + return insert(length(), str); + } + + template>> + CString& append(T str) & noexcept { + return insert(length(), str); + } + + CString&& append(char const c) && noexcept { + this->append(c); + return std::move(*this); + } + + template + CString&& append(const StringLiteral& str) && noexcept { + this->append(str); + return std::move(*this); + } + + CString&& append(const CString& str) && noexcept { + this->append(str); + return std::move(*this); + } + + template>> + CString&& append(T str) && noexcept { + this->append(str); + return std::move(*this); + } + + // operator+= + CString& operator+=(char const c) & noexcept { + return append(c); + } + + CString& operator+=(const CString& str) & noexcept { + return append(str); + } + template + CString& operator+=(const StringLiteral& str) & noexcept { + return append(str); + } + template >> + CString& operator+=(T str) & noexcept { + return append(str); + } - const_reference operator[](size_type pos) const noexcept { + const_reference operator[](size_type const pos) const noexcept { assert(pos < size()); return begin()[pos]; } - reference operator[](size_type pos) noexcept { + reference operator[](size_type const pos) noexcept { assert(pos < size()); return begin()[pos]; } - const_reference at(size_type pos) const noexcept { + const_reference at(size_type const pos) const noexcept { assert(pos < size()); return begin()[pos]; } - reference at(size_type pos) noexcept { + reference at(size_type const pos) noexcept { assert(pos < size()); return begin()[pos]; } @@ -167,20 +325,30 @@ class UTILS_PUBLIC CString { } // placement new declared as "throw" to avoid the compiler's null-check - inline void* operator new(size_t, void* ptr) { + void* operator new(size_t, void* ptr) { assert(ptr); return ptr; } - struct Hasher : private hashCStrings { - typedef CString argument_type; - typedef size_t result_type; - result_type operator()(const argument_type& s) const noexcept { - return hashCStrings::operator()(s.c_str()); - } - }; + // conversion to std::string_view + operator std::string_view() const noexcept { + return std::string_view{data(), size()}; + } private: + static void do_tracking(bool ctor); + static void track(bool ctor) { + if constexpr (TRACK_AND_LOG_ALLOCATIONS) { + do_tracking(ctor); + } + } + + CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept; + +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const CString& rhs); +#endif + struct Data { size_type length; }; @@ -191,17 +359,16 @@ class UTILS_PUBLIC CString { Data* mData; // Data is stored at mData[-1] }; + int compare(const std::string_view& rhs) const noexcept { + return std::string_view{data(), size()}.compare(rhs); + } + int compare(const CString& rhs) const noexcept { - size_type const lhs_size = size(); - size_type const rhs_size = rhs.size(); - if (lhs_size < rhs_size) return -1; - if (lhs_size > rhs_size) return 1; - return strncmp(data(), rhs.data(), size()); + return compare(std::string_view{rhs.data(), rhs.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())); + return lhs.compare(rhs) == 0; } friend bool operator!=(CString const& lhs, CString const& rhs) noexcept { return !(lhs == rhs); @@ -218,9 +385,125 @@ class UTILS_PUBLIC CString { friend bool operator<=(CString const& lhs, CString const& rhs) noexcept { return !(lhs > rhs); } + + friend bool operator==(CString const& lhs, std::string_view const& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator==(std::string_view const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(CString const& lhs, std::string_view const& rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator!=(std::string_view const& lhs, CString const& rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator<(CString const& lhs, std::string_view const& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator<(std::string_view const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(CString const& lhs, std::string_view const& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator>(std::string_view const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(CString const& lhs, std::string_view const& rhs) noexcept { + return !(lhs < rhs); + } + friend bool operator>=(std::string_view const& lhs, CString const& rhs) noexcept { + return !(lhs < rhs); + } + friend bool operator<=(CString const& lhs, std::string_view const& rhs) noexcept { + return !(lhs > rhs); + } + friend bool operator<=(std::string_view const& lhs, CString const& rhs) noexcept { + return !(lhs > rhs); + } }; -// implement this for your type for automatic conversion to CString. Failing to do so leads +// operator+ +inline CString operator+(CString lhs, const CString& rhs) { + lhs += rhs; + return lhs; +} + +inline CString operator+(CString lhs, const char* rhs) { + lhs += rhs; + return lhs; +} + +inline CString operator+(const char* lhs, CString rhs) { + rhs.insert(0, lhs); + return rhs; +} + +inline CString operator+(CString lhs, char const rhs) { + lhs += rhs; + return lhs; +} + +inline CString operator+(char const lhs, CString rhs) { + rhs.insert(0, lhs); + return rhs; +} + +// CString vs StringLiteral +template + bool operator==(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} == std::string_view{rhs, N - 1}; +} +template + bool operator!=(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} != std::string_view{rhs, N - 1}; +} +template + bool operator<(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} < std::string_view{rhs, N - 1}; +} +template + bool operator>(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} > std::string_view{rhs, N - 1}; +} +template + bool operator<=(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} <= std::string_view{rhs, N - 1}; +} +template + bool operator>=(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} >= std::string_view{rhs, N - 1}; +} + +// StringLiteral vs CString +template + bool operator==(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} == std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator!=(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} != std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator<(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} < std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator>(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} > std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator<=(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} <= std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator>=(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} >= std::string_view{rhs.data(), rhs.size()}; +} + + +// Implement this for your type for automatic conversion to CString. Failing to do so leads // to a compile-time failure. template CString to_string(T value) noexcept; @@ -244,9 +527,47 @@ class UTILS_PUBLIC FixedSizeString { pointer c_str() noexcept { return mData; } private: - value_type mData[N] = {0}; + value_type mData[N] = {}; }; } // namespace utils +// heterogeneous lookup support for associative containers +namespace std { + template <> + struct hash { + using is_transparent = void; // Enable heterogeneous lookup + + size_t operator()(const utils::CString& k) const noexcept { + return compute_hash(std::string_view(k)); + } + + template && + !std::is_same_v, utils::CString>>> + size_t operator()(const T& k) const noexcept { + return compute_hash(std::string_view(k)); + } + + private: + size_t compute_hash(std::string_view k) const noexcept { + size_t hash = 5381; + for (char const c : k) { + hash = (hash * 33u) ^ size_t(c); + } + return hash; + } + }; + + template<> + struct equal_to { + using is_transparent = void; // Enable heterogeneous lookup + + template + bool operator()(const T& lhs, const U& rhs) const { + return lhs == rhs; + } + }; +} + #endif // TNT_UTILS_CSTRING_H diff --git a/package/android/libs/filament/include/utils/CallStack.h b/package/android/libs/filament/include/utils/CallStack.h index 33ac0b50..f8366cfa 100644 --- a/package/android/libs/filament/include/utils/CallStack.h +++ b/package/android/libs/filament/include/utils/CallStack.h @@ -23,9 +23,11 @@ #include #include -#include namespace utils { +namespace io { +class ostream; +} /** * CallStack captures the current's thread call stack. @@ -68,10 +70,10 @@ class CallStack { intptr_t operator [](size_t index) const; /** Demangles a C++ type name */ - static utils::CString demangleTypeName(const char* mangled); + static CString demangleTypeName(const char* mangled); template - static utils::CString typeName() { + static CString typeName() { #if UTILS_HAS_RTTI return demangleTypeName(typeid(T).name()); #else @@ -84,34 +86,34 @@ class CallStack { * 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); + friend io::ostream& operator <<(io::ostream& stream, const CallStack& callstack); bool operator <(const CallStack& rhs) const; - inline bool operator >(const CallStack& rhs) const { + bool operator >(const CallStack& rhs) const { return rhs < *this; } - inline bool operator !=(const CallStack& rhs) const { + bool operator !=(const CallStack& rhs) const { return *this < rhs || rhs < *this; } - inline bool operator >=(const CallStack& rhs) const { + bool operator >=(const CallStack& rhs) const { return !operator <(rhs); } - inline bool operator <=(const CallStack& rhs) const { + bool operator <=(const CallStack& rhs) const { return !operator >(rhs); } - inline bool operator ==(const CallStack& rhs) const { + 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 CString demangle(const char* mangled); static constexpr size_t NUM_FRAMES = 20; diff --git a/package/android/libs/filament/include/utils/FixedCapacityVector.h b/package/android/libs/filament/include/utils/FixedCapacityVector.h index f3990124..fd920876 100644 --- a/package/android/libs/filament/include/utils/FixedCapacityVector.h +++ b/package/android/libs/filament/include/utils/FixedCapacityVector.h @@ -17,10 +17,11 @@ #ifndef TNT_UTILS_FIXEDCAPACITYVECTOR_H #define TNT_UTILS_FIXEDCAPACITYVECTOR_H +#include #include #include -#include +#include #include #include #include @@ -40,6 +41,11 @@ namespace utils { +class UTILS_PUBLIC FixedCapacityVectorBase { +protected: + UTILS_NORETURN static void capacityCheckFailed(size_t capacity, size_t size); +}; + /** * 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() @@ -56,7 +62,7 @@ namespace utils { * the optional value argument, e.g. FixedCapacityVector(4, 0) or foo.resize(4, 0). */ template, bool CapacityCheck = true> -class UTILS_PUBLIC FixedCapacityVector { +class UTILS_PUBLIC FixedCapacityVector : protected FixedCapacityVectorBase { public: using allocator_type = A; using value_type = T; @@ -84,7 +90,7 @@ class UTILS_PUBLIC FixedCapacityVector { FixedCapacityVector() = default; explicit FixedCapacityVector(const allocator_type& allocator) noexcept - : mCapacityAllocator({}, allocator) { + : mCapacityAllocator(0, allocator) { } explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type()) @@ -122,6 +128,14 @@ class UTILS_PUBLIC FixedCapacityVector { this->swap(rhs); } + FixedCapacityVector(utils::Slice rhs, + const allocator_type& alloc = allocator_type()) + : mSize(rhs.size()), + mCapacityAllocator(mSize, alloc) { + mData = this->allocator().allocate(this->capacity()); + std::uninitialized_copy(rhs.cbegin(), rhs.cend(), begin()); + } + ~FixedCapacityVector() noexcept { destroy(begin(), end()); allocator().deallocate(data(), capacity()); @@ -140,6 +154,29 @@ class UTILS_PUBLIC FixedCapacityVector { return *this; } + bool operator==(const FixedCapacityVector& rhs) const noexcept { + if (this == &rhs) { + return true; + } + if (size() != rhs.size()) { + return false; + } + return std::equal(begin(), end(), rhs.begin()); + } + + Slice as_slice() noexcept { + return { begin(), end() }; + } + + Slice as_slice() const noexcept { + return { cbegin(), cend() }; + } + + template> + inline size_t hash() const noexcept { + return as_slice().template hash(); + } + allocator_type get_allocator() const noexcept { return mCapacityAllocator.second(); } @@ -266,7 +303,7 @@ class UTILS_PUBLIC FixedCapacityVector { mSize = 0; } - void resize(size_type count) { + void resize(size_type const count) { assertCapacityForSize(count); if constexpr(std::is_trivially_constructible_v && std::is_trivially_destructible_v) { @@ -277,12 +314,12 @@ class UTILS_PUBLIC FixedCapacityVector { } } - void resize(size_type count, const_reference v) { + void resize(size_type const count, const_reference v) { assertCapacityForSize(count); resize_non_trivial(count, v); } - void swap(FixedCapacityVector& other) { + void swap(FixedCapacityVector& other) noexcept { using std::swap; swap(mData, other.mData); swap(mSize, other.mSize); @@ -299,6 +336,16 @@ class UTILS_PUBLIC FixedCapacityVector { } } + UTILS_NOINLINE + void shrink_to_fit() { + if (size() < capacity()) { + FixedCapacityVector t(construct_with_capacity, size(), allocator()); + t.mSize = size(); + std::uninitialized_move(begin(), end(), t.begin()); + this->swap(t); + } + } + private: enum construct_with_capacity_tag{ construct_with_capacity }; @@ -316,16 +363,16 @@ class UTILS_PUBLIC FixedCapacityVector { return mCapacityAllocator.second(); } - iterator assertCapacityForSize(size_type s) { + iterator assertCapacityForSize(size_type const 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()); + if (UTILS_VERY_UNLIKELY(capacity() < s)) { + capacityCheckFailed(capacity(), s); + } } return end(); } - inline void construct(iterator first, iterator last) noexcept { + void construct(iterator const first, iterator const last) noexcept { // we check for triviality here so that the implementation could be non-inline if constexpr(!std::is_trivially_constructible_v) { construct_non_trivial(first, last); @@ -348,7 +395,7 @@ class UTILS_PUBLIC FixedCapacityVector { } - inline void destroy(iterator first, iterator last) noexcept { + void destroy(iterator const first, iterator const last) noexcept { // we check for triviality here so that the implementation could be non-inline if constexpr(!std::is_trivially_destructible_v) { destroy_non_trivial(first, last); @@ -409,7 +456,7 @@ class UTILS_PUBLIC FixedCapacityVector { explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { } SizeTypeWrapper& operator=(TYPE rhs) noexcept { value = rhs; return *this; } SizeTypeWrapper& operator=(SizeTypeWrapper& rhs) noexcept = delete; - operator TYPE() const noexcept { return value; } + operator TYPE() const noexcept { return value; } // NOLINT(*-explicit-constructor) }; pointer mData{}; @@ -419,4 +466,13 @@ class UTILS_PUBLIC FixedCapacityVector { } // namespace utils +namespace std { +template +struct hash> { + inline size_t operator()(utils::FixedCapacityVector const& lhs) const noexcept { + return lhs.hash(); + } +}; +} // namespace std + #endif // TNT_UTILS_FIXEDCAPACITYVECTOR_H diff --git a/package/android/libs/filament/include/utils/Hash.h b/package/android/libs/filament/include/utils/Hash.h new file mode 100644 index 00000000..f1ff3aff --- /dev/null +++ b/package/android/libs/filament/include/utils/Hash.h @@ -0,0 +1,149 @@ +/* + * 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 // for std::hash +#include +#include +#include + +#include +#include + +namespace utils::hash { + +// The standard CRC-32 polynomial (used in Ethernet, ZIP, PNG, etc.) +static constexpr uint32_t CRC32_POLYNOMIAL = 0xEDB88320; + +// Generates the lookup table for the CRC-32 algorithm. +inline void crc32GenerateTable(std::vector& table) { + if (table.size() != 256) { + table.resize(256); + } + + for (uint32_t i = 0; i < 256; ++i) { + uint32_t crc = i; + for (int j = 0; j < 8; ++j) { + if (crc & 1) { + crc = (crc >> 1) ^ CRC32_POLYNOMIAL; + } else { + crc = crc >> 1; + } + } + table[i] = crc; + } +} + +// Updates the CRC-32 value with a new chunk of data. This function can be called multiple times to +// process a large buffer in smaller, separate parts. +// @param previous_crc The CRC value returned from the previous call to `crc32Update`. +// @param data A pointer to the data buffer for this chunk. +// @param length The length of the data buffer in bytes. +// @return The new CRC value. +inline uint32_t crc32Update(uint32_t previous_crc, const void* data, size_t length, const std::vector& table) { + uint32_t crc = ~previous_crc; + const uint8_t* current = static_cast(data); + for (size_t i = 0; i < length; ++i) { + // The core CRC-32 calculation step. + crc = (crc >> 8) ^ table[(crc ^ current[i]) & 0xFF]; + } + return ~crc; +} + +inline size_t combine(size_t lhs, size_t rhs) noexcept { + std::pair const p{ lhs, rhs }; + return std::hash{}({ (char*)&p, sizeof(p) }); +} + +// Hash function that takes an arbitrary swath of word-aligned data. +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; +} + +// The hash yields the same result for a given byte sequence regardless of alignment. +inline uint32_t murmurSlow(const uint8_t* key, size_t byteCount, uint32_t seed) noexcept { + const size_t wordCount = (byteCount + 3) / 4; + const uint8_t* const last = key + byteCount; + + // The remainder is identical to murmur3() except an inner loop safely "reads" an entire word. + uint32_t h = seed; + size_t wc = wordCount; + do { + uint32_t k = 0; + for (int i = 0; i < 4 && key < last; ++i, ++key) { + k >>= 8; + k |= uint32_t(*key) << 24; + } + k *= 0xcc9e2d51u; + k = (k << 15u) | (k >> 17u); + k *= 0x1b873593u; + h ^= k; + h = (h << 13u) | (h >> 19u); + h = (h * 5u) + 0xe6546b64u; + } while (--wc); + h ^= wordCount; + h ^= h >> 16u; + h *= 0x85ebca6bu; + h ^= h >> 13u; + h *= 0xc2b2ae35u; + h ^= h >> 16u; + return h; +} + +template +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 +inline void combine(size_t& seed, const T& v) noexcept { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u); +} + +// combines two hashes together, faster but less good +template +inline void combine_fast(size_t& seed, const T& v) noexcept { + std::hash hasher; + seed ^= hasher(v) << 1u; +} + +} // namespace utils::hash + +#endif // TNT_UTILS_HASH_H diff --git a/package/android/libs/filament/include/utils/ImmutableCString.h b/package/android/libs/filament/include/utils/ImmutableCString.h new file mode 100644 index 00000000..48fbf430 --- /dev/null +++ b/package/android/libs/filament/include/utils/ImmutableCString.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2025 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_IMMUTABLECSTRING_H +#define TNT_UTILS_IMMUTABLECSTRING_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace utils { + + +class UTILS_PUBLIC ImmutableCString { + static constexpr bool TRACK_AND_LOG_ALLOCATIONS = false; + + template + static constexpr bool is_char_pointer_v = + std::is_pointer_v && std::is_same_v>>; + +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*; + + ImmutableCString() noexcept { + track(true, mIsStatic); + } + + // The string can't have NULs in it. + template + ImmutableCString(const char (&str)[N]) noexcept : mData(str), mSize(N - 1) { // NOLINT(*-explicit-constructor) + track(true, mIsStatic); + } + + // This constructor can be used if the string has NULs in it. + template + ImmutableCString(const char (&str)[N], size_t const length) noexcept + : mData(str), mSize(length) { + track(true, mIsStatic); + } + + template>> + explicit ImmutableCString(T cstr) { + if (cstr) { + initializeFrom(cstr, strlen(cstr)); + } + track(true, mIsStatic); + } + + ImmutableCString(const char* cstr, size_t const length) { + initializeFrom(cstr, length); + track(true, mIsStatic); + } + + ImmutableCString(StaticString const& str) // NOLINT(*-explicit-constructor) + : mData(str.data()), mSize(str.size()) { + track(true, mIsStatic); + } + + ImmutableCString(const ImmutableCString& other) { + if (other.mIsStatic) { + mIsStatic = other.mIsStatic; + mSize = other.mSize; + mData = other.mData; + } else { + initializeFrom(other.mData, other.mSize); + } + track(true, mIsStatic); + } + + ImmutableCString(ImmutableCString&& other) noexcept { + track(true, mIsStatic); + this->swap(other); + } + + ImmutableCString& operator=(const ImmutableCString& other); + + ImmutableCString& operator=(ImmutableCString&& other) noexcept; + + ~ImmutableCString() { + track(false, mIsStatic); + if (!mIsStatic) { + free(const_cast(mData)); + } + } + + bool isStatic() const noexcept { return mIsStatic; } + bool isDynamic() const noexcept { return !mIsStatic; } + + const_pointer c_str_safe() const noexcept { return mData; } + const_pointer c_str() const noexcept { return mData; } + const_pointer data() const noexcept { return mData; } + size_type size() const noexcept { return mSize; } + size_type length() const noexcept { return mSize; } + bool empty() const noexcept { return mSize == 0; } + + const_iterator begin() const noexcept { return mData; } + const_iterator end() const noexcept { return mData + mSize; } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_reference operator[](size_type const pos) const noexcept { + assert(pos < mSize); + return mData[pos]; + } + + const_reference at(size_type const pos) const noexcept { + assert(pos < mSize); + return mData[pos]; + } + + const_reference front() const noexcept { + assert(mSize > 0); + return mData[0]; + } + + const_reference back() const noexcept { + assert(mSize > 0); + return mData[mSize - 1]; + } + + void swap(ImmutableCString& other) noexcept { + std::swap(mData, other.mData); + std::swap(mSize, other.mSize); + std::swap(mIsStatic, other.mIsStatic); + } + +private: + static void do_tracking(bool ctor, bool is_static); + static void track(bool ctor, bool is_static) { + if constexpr (TRACK_AND_LOG_ALLOCATIONS) { + do_tracking(ctor, is_static); + } + } + +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs); +#endif + + void initializeFrom(const char* cstr, size_t length); + + int compare(const ImmutableCString& rhs) const noexcept { + return std::string_view{ mData, mSize }.compare({ rhs.mData, rhs.mSize }); + } + + char const* mData = ""; + uint32_t mSize = 0; + bool mIsStatic = true; + + friend bool operator==(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) != 0; + } + friend bool operator<(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator<=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) <= 0; + } + friend bool operator>=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) >= 0; + } +}; + +static_assert(sizeof(ImmutableCString) <= 16, "ImmutableCString should be 16 bytes or less"); + +} // namespace utils + +#endif //TNT_UTILS_IMMUTABLECSTRING_H diff --git a/package/android/libs/filament/include/utils/InternPool.h b/package/android/libs/filament/include/utils/InternPool.h new file mode 100644 index 00000000..b9ccccbd --- /dev/null +++ b/package/android/libs/filament/include/utils/InternPool.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2025 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_INTERNPOOL_H +#define TNT_UTILS_INTERNPOOL_H + +#include +#include +#include +#include +#include + +#include + +namespace utils { + +/** A reference-counted intern pool of slices of T. */ +template> +class InternPool { + struct HashSlice { + inline size_t operator()(Slice const& slice) const noexcept { + return slice.template hash(); + } + }; + + struct Entry { + uint32_t referenceCount; + FixedCapacityVector value; + }; + + using Map = tsl::robin_map, Entry, HashSlice>; + + static constexpr const char* UTILS_NONNULL MISSING_ENTRY_ERROR_STRING = + "InternPool is missing entry"; + +public: + InternPool() = default; + InternPool(InternPool const& rhs) = delete; + InternPool& operator=(InternPool const& rhs) = delete; + InternPool(InternPool&& rhs) = default; + InternPool& operator=(InternPool&& rhs) = default; + + /** Acquire an interned copy of value. */ + Slice acquire(Slice slice, size_t hash) noexcept { + if (slice.empty()) { + return { nullptr, nullptr }; + } + auto it = mMap.find(slice, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return it.key(); + } + FixedCapacityVector value(slice); + // TODO: how to use above computed hash here? + return mMap.insert({ value.as_slice(), Entry{ 1, std::move(value) } }).first.key(); + } + + inline Slice acquire(Slice slice) noexcept { + return acquire(slice, HashSlice{}(slice)); + } + + Slice acquire(FixedCapacityVector&& value, size_t hash) noexcept { + if (value.empty()) { + return { nullptr, nullptr }; + } + Slice slice = value.as_slice(); + auto it = mMap.find(slice, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return it.key(); + } + // TODO: how to use above computed hash here? + return mMap.insert({ slice, Entry{ 1, std::move(value) } }).first.key(); + } + + inline Slice acquire(FixedCapacityVector&& value) noexcept { + size_t hash = HashSlice{}(value.as_slice()); + return acquire(std::move(value), hash); + } + + inline Slice acquire(FixedCapacityVector const& value, size_t hash) noexcept { + return acquire(value.as_slice(), hash); + } + + inline Slice acquire(FixedCapacityVector const& value) noexcept { + Slice slice = value.as_slice(); + return acquire(slice, HashSlice{}(slice)); + } + + /** Release interned value. */ + void release(Slice slice, size_t hash) { + if (slice.empty()) { + return; + } + auto it = mMap.find(slice, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + if (--it.value().referenceCount == 0) { + // TODO: change to erase_fast + mMap.erase(it); + } + } + + inline void release(Slice slice) noexcept { + return release(slice, HashSlice{}(slice)); + } + + inline void release(FixedCapacityVector const& value, size_t hash) noexcept { + return release(value.as_slice(), hash); + } + + inline void release(FixedCapacityVector const& value) noexcept { + Slice slice = value.as_slice(); + return release(slice, HashSlice{}(slice)); + } + + /** Returns true if the pool is empty. */ + inline bool empty() const noexcept { return mMap.empty(); } + + /** Returns hash of value. */ + static size_t hash(Slice slice) noexcept { + return HashSlice{}(slice); + } + + static size_t hash(FixedCapacityVector const& value) noexcept { + return HashSlice{}(value.as_slice()); + } + +private: + Map mMap; +}; + +} // namespace utils + +#endif // TNT_UTILS_INTERNPOOL_H diff --git a/package/android/libs/filament/include/utils/Invocable.h b/package/android/libs/filament/include/utils/Invocable.h index 49b43071..08d42968 100644 --- a/package/android/libs/filament/include/utils/Invocable.h +++ b/package/android/libs/filament/include/utils/Invocable.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef TNT_UTILS_INVOKABLE_H -#define TNT_UTILS_INVOKABLE_H +#ifndef TNT_UTILS_INVOCABLE_H +#define TNT_UTILS_INVOCABLE_H #include #include @@ -23,6 +23,9 @@ #include namespace utils { +namespace io { +class ostream; +} /* * Invocable is a move-only general purpose function wrapper. Instances can @@ -48,15 +51,21 @@ template using EnableIfFnMatchesInvocable = std::enable_if_t; #endif +class InvocableBase { +protected: + static io::ostream& printInvocable(io::ostream& out, const char* name); +}; + template class Invocable; template -class Invocable { +class Invocable : protected InvocableBase { public: // Creates an Invocable that does not contain a functor. // Will evaluate to false. Invocable() = default; + Invocable(std::nullptr_t) noexcept {} ~Invocable() noexcept; @@ -69,6 +78,7 @@ class Invocable { Invocable& operator=(const Invocable&) = delete; Invocable& operator=(Invocable&& rhs) noexcept; + Invocable& operator=(std::nullptr_t) noexcept; // Invokes the invocable with the args passed in. // If the Invocable is empty, this will assert. @@ -81,6 +91,11 @@ class Invocable { explicit operator bool() const noexcept; private: +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const Invocable&) { + return printInvocable(out, "Invocable<>"); // TODO: is there a way to do better here? + } +#endif void* mInvocable = nullptr; void (*mDeleter)(void*) = nullptr; R (* mInvoker)(void*, Args...) = nullptr; @@ -128,6 +143,17 @@ Invocable& Invocable::operator=(Invocable&& rhs) noexcep return *this; } +template +Invocable& Invocable::operator=(std::nullptr_t) noexcept { + if (mDeleter) { + mDeleter(mInvocable); + } + mInvocable = nullptr; + mDeleter = nullptr; + mInvoker = nullptr; + return *this; +} + template template R Invocable::operator()(OperatorArgs&& ... args) { @@ -149,4 +175,4 @@ Invocable::operator bool() const noexcept { } // namespace utils -#endif // TNT_UTILS_INVOKABLE_H +#endif // TNT_UTILS_INVOCABLE_H diff --git a/package/android/libs/filament/include/utils/Logger.h b/package/android/libs/filament/include/utils/Logger.h new file mode 100644 index 00000000..b385c85b --- /dev/null +++ b/package/android/libs/filament/include/utils/Logger.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2025 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_LOGGER_H +#define TNT_UTILS_LOGGER_H + +// Logger.h provides a subset of the Abseil logging API, offering the following macros: + +// **LOG(severity)**: Logs a message at the specified severity level. +// **DLOG(severity)**: Logs a message at the specified severity level only in debug builds. + +// Supported `severity` levels are: +// * `INFO` +// * `WARNING` +// * `ERROR` +// * `FATAL` + +// For programmatic control over logging severity, use the `LEVEL` macro: + +// **LOG(LEVEL(expression))**: Logs a message at a severity level determined by the `expression`. +// The `expression` must return a `utils::LogSeverity` value, which is equivalent to +// `absl::LogSeverity`. + +#if defined(FILAMENT_USE_ABSEIL_LOGGING) + +#include +#include + +namespace utils { +using absl::LogSeverity; +} + +#else + +#include +#include + +namespace utils { + +enum class LogSeverity : int { + kInfo = 0, + kWarning = 1, + kError = 2, + kFatal = 3, +}; + +template +class LogLine { +public: + explicit LogLine(Stream& stream) + : mStream(stream) {} + + LogLine(const LogLine&) = delete; + LogLine(LogLine&&) = delete; + LogLine& operator=(const LogLine&) = delete; + LogLine& operator=(LogLine&&) = delete; + + ~LogLine() noexcept { mStream << io::endl; } + + template + LogLine& operator<<(T&& value) { + mStream << std::forward(value); + return *this; + } + +private: + Stream& mStream; +}; + +static inline io::ostream& getLogStream(LogSeverity severity) { + switch (severity) { + case LogSeverity::kInfo: + return slog.d; + case LogSeverity::kWarning: + return slog.w; + case LogSeverity::kError: + return slog.e; + case LogSeverity::kFatal: + return slog.e; + default: + return slog.d; + } +} + +struct NoopStream final { + template + NoopStream& operator<<(const T&) { + return *this; + } +}; + +#define LOG(severity) LOG_IMPL_##severity + +#ifndef NDEBUG +#define DLOG(severity) DLOG_IMPL_##severity +#else +#define DLOG(severity) utils::NoopStream() +#endif + +#define DLOG_IMPL_INFO utils::LogLine(utils::slog.d) +#define DLOG_IMPL_WARNING utils::LogLine(utils::slog.d) +#define DLOG_IMPL_ERROR utils::LogLine(utils::slog.d) +#define DLOG_IMPL_LEVEL(severity) utils::LogLine(((void) severity, utils::slog.d)) + +#define LOG_IMPL_INFO utils::LogLine(utils::slog.i) +#define LOG_IMPL_WARNING utils::LogLine(utils::slog.w) +#define LOG_IMPL_ERROR utils::LogLine(utils::slog.e) +#define LOG_IMPL_LEVEL(severity) utils::LogLine(getLogStream(severity)) + +}// namespace utils +#endif + +#endif// TNT_UTILS_LOGGER_H diff --git a/package/android/libs/filament/include/utils/MonotonicRingMap.h b/package/android/libs/filament/include/utils/MonotonicRingMap.h new file mode 100644 index 00000000..01ead770 --- /dev/null +++ b/package/android/libs/filament/include/utils/MonotonicRingMap.h @@ -0,0 +1,157 @@ +/* +* Copyright (C) 2025 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_MONOTONICRINGMAP_H +#define TNT_UTILS_MONOTONICRINGMAP_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace utils { + +/** + * A map-like container with a fixed capacity and monotonically increasing keys. + * When the map is full, inserting a new element overwrites the oldest one. + * This container doesn't allocate any memory on the heap. + * Lookups are O(log N). + */ +template +class MonotonicRingMap { +public: + using key_type = KEY; //!< The key type. + using mapped_type = VALUE; //!< The value type. + using value_type = std::pair; //!< The key-value pair type. + + //! Creates an empty map. + MonotonicRingMap() noexcept = default; + + //! Returns the number of elements in the map. + size_t size() const noexcept { return mSize; } + + //! Returns the maximum number of elements the map can hold. + static constexpr size_t capacity() noexcept { return N; } + + //! Returns true if the map is empty. + bool empty() const noexcept { return mSize == 0; } + + //! Returns true if the map is full. + bool full() const noexcept { return mSize == N; } + + //! Clears the map entirely. + void clear() noexcept { mSize = 0; mHead = 0; } + + /** + * Inserts a new key-value pair. + * The key must be greater than the key of the last inserted element. + * If the map is full, the oldest element is overwritten. + * @param key The key to insert. + * @param value The value to associate with the key. + */ + UTILS_NOINLINE void insert(key_type key, mapped_type value) { + assert(empty() || key > back().first); // assert monotonic + if (UTILS_LIKELY(full())) { + // container is full, replace the oldest element + mStorage[mHead] = { key, value }; + mHead = (mHead + 1) % N; + } else { + // container is not full, add to the end + const uint32_t index = (mHead + mSize) % N; + mStorage[index] = { key, value }; + mSize++; + } + } + + /** + * Finds a value by its key. + * @param key The key to look for. + * @return A pointer to the value if the key is found, nullptr otherwise. + */ + mapped_type* find(key_type key) { + return const_cast(static_cast(this)->find(key)); + } + + /** + * Finds a value by its key. + * @param key The key to look for. + * @return A pointer to the const value if the key is found, nullptr otherwise. + */ + UTILS_NOINLINE const mapped_type* find(key_type key) const { + if (empty()) { + return nullptr; + } + + if (key < front().first || key > back().first) { + return nullptr; + } + + const auto comparator = [](const value_type& element, key_type k) { + return element.first < k; + }; + + const auto endOfStorage = mStorage.cbegin() + N; + const auto headIter = mStorage.cbegin() + mHead; + + if (mHead + mSize <= N) { + // The logical sequence is contiguous in memory + const auto logicalEnd = headIter + mSize; + auto it = std::lower_bound(headIter, logicalEnd, key, comparator); + if (it != logicalEnd && it->first == key) { + return &it->second; + } + } else { // Wrapped around + // First part: mStorage[mHead...N-1] + auto it1 = std::lower_bound(headIter, endOfStorage, key, comparator); + if (it1 != endOfStorage && it1->first == key) { + return &it1->second; + } + + // Second part: mStorage[0...head-1] + const auto wrapPartEndIter = mStorage.cbegin() + ((mHead + mSize) % N); + auto it2 = std::lower_bound(mStorage.cbegin(), wrapPartEndIter, key, comparator); + if (it2 != wrapPartEndIter && it2->first == key) { + return &it2->second; + } + } + return nullptr; + } + + //! Returns a reference to the oldest element. + const value_type& front() const { + assert(!empty()); + return mStorage[mHead]; + } + + //! Returns a reference to the newest element. + const value_type& back() const { + assert(!empty()); + return mStorage[(mHead + mSize - 1) % N]; + } + +private: + std::array mStorage; + uint32_t mSize = 0; + uint32_t mHead = 0; +}; + +} // namespace utils + +#endif // TNT_UTILS_MONOTONICRINGMAP_H diff --git a/package/android/libs/filament/include/utils/NameComponentManager.h b/package/android/libs/filament/include/utils/NameComponentManager.h index 4ac7435a..403cc011 100644 --- a/package/android/libs/filament/include/utils/NameComponentManager.h +++ b/package/android/libs/filament/include/utils/NameComponentManager.h @@ -47,7 +47,7 @@ class EntityManager; * printf("%s\n", names->getName(names->getInstance(myEntity)); * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager { +class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager { public: using Instance = EntityInstance; @@ -97,7 +97,7 @@ class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager const char* getName(Instance instance) const noexcept; void gc(EntityManager& em) noexcept { - SingleInstanceComponentManager::gc(em, [this](Entity e) { + SingleInstanceComponentManager::gc(em, [this](Entity e) { removeComponent(e); }); } diff --git a/package/android/libs/filament/include/utils/Panic.h b/package/android/libs/filament/include/utils/Panic.h index c658da4b..acd19468 100644 --- a/package/android/libs/filament/include/utils/Panic.h +++ b/package/android/libs/filament/include/utils/Panic.h @@ -17,14 +17,28 @@ #ifndef TNT_UTILS_PANIC_H #define TNT_UTILS_PANIC_H +#ifdef FILAMENT_PANIC_USES_ABSL +# if FILAMENT_PANIC_USES_ABSL +# include "absl/log/log.h" +# define FILAMENT_CHECK_PRECONDITION CHECK +# define FILAMENT_CHECK_POSTCONDITION CHECK +# define FILAMENT_CHECK_ARITHMETIC CHECK +# endif +#endif + #include #include +#include #include +#include #ifdef __EXCEPTIONS # define UTILS_EXCEPTIONS 1 #else +# ifdef UTILS_EXCEPTIONS +# error UTILS_EXCEPTIONS is already defined! +# endif #endif /** @@ -280,12 +294,24 @@ class UTILS_PUBLIC Panic { */ virtual const char* what() const noexcept = 0; + /** + * Get the type of the panic (e.g. "Precondition") + * @return a C string containing the type of panic + */ + virtual const char* getType() const noexcept = 0; + /** * Get the reason string for the panic * @return a C string containing the reason for the panic */ virtual const char* getReason() const noexcept = 0; + /** + * Get a version of the reason string that is guaranteed to be constructed from literal + * strings only; it will contain no runtime information. + */ + virtual const char* getReasonLiteral() const noexcept = 0; + /** * Get the function name where the panic was detected. On debug build the fully qualified * function name is returned; on release builds only the function name is. @@ -327,14 +353,16 @@ class UTILS_PUBLIC Panic { * The TPanic<> class implements the std::exception protocol as well as the Panic * interface common to all exceptions thrown by the framework. */ -template +template class UTILS_PUBLIC TPanic : public Panic { public: // std::exception protocol const char* what() const noexcept override; // Panic interface + const char* getType() const noexcept override; const char* getReason() const noexcept override; + const char* getReasonLiteral() const noexcept override; const char* getFunction() const noexcept override; const char* getFile() const noexcept override; int getLine() const noexcept override; @@ -348,13 +376,15 @@ class UTILS_PUBLIC TPanic : public Panic { * @param function the name of the function where the error was detected * @param file the file where the above function in implemented * @param line the line in the above file where the error was detected - * @param format printf style string describing the error + * @param literal a literal version of the error message + * @param format printf style format string describing the error + * @param ... printf style arguments * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC * @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC * @see setMode() */ - static void panic(char const* function, char const* file, int line, const char* format, ...) - UTILS_NORETURN; + static void panic(char const* function, char const* file, int line, char const* literal, + const char* format, ...) UTILS_NORETURN; /** * Depending on the mode set, either throws an exception of type T with the given reason plus @@ -363,43 +393,43 @@ class UTILS_PUBLIC TPanic : public Panic { * @param function the name of the function where the error was detected * @param file the file where the above function in implemented * @param line the line in the above file where the error was detected - * @param s std::string describing the error + * @param literal a literal version of the error message + * @param reason std::string describing the error * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC * @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC * @see setMode() */ - static inline void panic(char const* function, char const* file, int line, const std::string& s) - UTILS_NORETURN { - panic(function, file, line, s.c_str()); - } - -protected: - /** - * Creates a Panic. - * @param reason a description of the cause of the error - */ - explicit TPanic(std::string reason); + static void panic( + char const* function, char const* file, int line, char const* literal, + std::string reason) UTILS_NORETURN; +private: /** * Creates a Panic with extra information about the error-site. * @param function the name of the function where the error was detected * @param file the file where the above function in implemented * @param line the line in the above file where the error was detected + * @param literal a literal version of the error message * @param reason a description of the cause of the error */ - TPanic(char const* function, char const* file, int line, std::string reason); + TPanic(char const* function, char const* file, int line, char const* literal, + std::string reason); + friend class PreconditionPanic; + friend class PostconditionPanic; + friend class ArithmeticPanic; + +protected: ~TPanic() override; private: - void buildMessage(); - - CallStack m_callstack; - std::string m_reason; - char const* const m_function = nullptr; - char const* const m_file = nullptr; - const int m_line = -1; - mutable std::string m_msg; + char const* const mFile = nullptr; // file where the panic happened + char const* const mFunction = nullptr; // function where the panic happened + int const mLine = -1; // line where the panic happened + std::string mLiteral; // reason for the panic, built only from literals + std::string mReason; // reason for the panic + mutable std::string mWhat; // fully formatted reason + CallStack mCallstack; }; namespace details { @@ -416,11 +446,12 @@ void panicLog( * ASSERT_PRECONDITION uses this Panic to report a precondition failure. * @see ASSERT_PRECONDITION */ -class UTILS_PUBLIC PreconditionPanic : public TPanic { +class UTILS_PUBLIC PreconditionPanic final : public TPanic { // Programming error, can be avoided // e.g.: invalid arguments - using TPanic::TPanic; - friend class TPanic; + using TPanic::TPanic; + friend class TPanic; + constexpr static auto type = "Precondition"; }; /** @@ -429,11 +460,12 @@ class UTILS_PUBLIC PreconditionPanic : public TPanic { * ASSERT_POSTCONDITION uses this Panic to report a postcondition failure. * @see ASSERT_POSTCONDITION */ -class UTILS_PUBLIC PostconditionPanic : public TPanic { +class UTILS_PUBLIC PostconditionPanic final : public TPanic { // Usually only detectable at runtime - // e.g.: dead-lock would occur, arithmetic errors - using TPanic::TPanic; - friend class TPanic; + // e.g.: deadlock would occur, arithmetic errors + using TPanic::TPanic; + friend class TPanic; + constexpr static auto type = "Postcondition"; }; /** @@ -442,13 +474,100 @@ class UTILS_PUBLIC PostconditionPanic : public TPanic { * ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure. * @see ASSERT_ARITHMETIC */ -class UTILS_PUBLIC ArithmeticPanic : public TPanic { +class UTILS_PUBLIC ArithmeticPanic final : public TPanic { // A common case of post-condition error // e.g.: underflow, overflow, internal computations errors - using TPanic::TPanic; - friend class TPanic; + using TPanic::TPanic; + friend class TPanic; + constexpr static auto type = "Arithmetic"; +}; + +namespace details { + +struct Voidify final { + template + void operator&&(const T&) const&& {} +}; + +class UTILS_PUBLIC PanicStream { +public: + PanicStream( + char const* function, + char const* file, + int line, + char const* message) noexcept; + + ~PanicStream(); + + PanicStream& operator<<(short value) noexcept; + PanicStream& operator<<(unsigned short value) noexcept; + + PanicStream& operator<<(char value) noexcept; + PanicStream& operator<<(unsigned char value) noexcept; + + PanicStream& operator<<(int value) noexcept; + PanicStream& operator<<(unsigned int value) noexcept; + + PanicStream& operator<<(long value) noexcept; + PanicStream& operator<<(unsigned long value) noexcept; + + PanicStream& operator<<(long long value) noexcept; + PanicStream& operator<<(unsigned long long value) noexcept; + + PanicStream& operator<<(float value) noexcept; + PanicStream& operator<<(double value) noexcept; + PanicStream& operator<<(long double value) noexcept; + + PanicStream& operator<<(bool value) noexcept; + + PanicStream& operator<<(const void* value) noexcept; + + PanicStream& operator<<(const char* value) noexcept; + PanicStream& operator<<(const unsigned char* value) noexcept; + + PanicStream& operator<<(std::string const& value) noexcept; + PanicStream& operator<<(std::string_view const& value) noexcept; + +protected: + io::sstream mStream; + char const* mFunction; + char const* mFile; + int mLine; + char const* mLiteral; +}; + +template +class TPanicStream : public PanicStream { +public: + using PanicStream::PanicStream; + ~TPanicStream() noexcept(false) UTILS_NORETURN { + T::panic(mFunction, mFile, mLine, mLiteral, mStream.c_str()); + } +}; + +template +class FlagGuardedStream : public PanicStream { +public: + FlagGuardedStream(bool const enable, char const* function, char const* file, int const line, + char const* condition) + : PanicStream(function, file, line, condition), + mEnablePanic(enable) {} + ~FlagGuardedStream() noexcept(false) { + if (mEnablePanic) { + PanicType::panic(mFunction, mFile, mLine, mLiteral, mStream.c_str()); + } else { + logWarning(); + } + } + +private: + void logWarning() noexcept; + + bool mEnablePanic; }; +} // namespace details + // ----------------------------------------------------------------------------------------------- } // namespace utils @@ -460,37 +579,84 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { # define PANIC_FUNCTION __func__ #endif + +#define FILAMENT_CHECK_CONDITION_IMPL(cond) \ + switch (0) \ + case 0: \ + default: \ + UTILS_VERY_LIKELY(cond) ? (void)0 : ::utils::details::Voidify()&& + +#define FILAMENT_PANIC_IMPL(message, TYPE) \ + ::utils::details::TPanicStream<::utils::TYPE>(PANIC_FUNCTION, PANIC_FILE(__FILE__), __LINE__, message) + +#ifndef FILAMENT_CHECK_PRECONDITION +#define FILAMENT_CHECK_PRECONDITION(condition) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, PreconditionPanic) +#endif + +#ifndef FILAMENT_CHECK_POSTCONDITION +#define FILAMENT_CHECK_POSTCONDITION(condition) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, PostconditionPanic) +#endif + +#ifndef FILAMENT_CHECK_ARITHMETIC +#define FILAMENT_CHECK_ARITHMETIC(condition) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, ArithmeticPanic) +#endif + +#define FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, TYPE) \ + ::utils::details::FlagGuardedStream<::utils::TYPE>(flag, PANIC_FUNCTION, PANIC_FILE(__FILE__), \ + __LINE__, #condition) + +#ifndef FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION +#define FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(condition, flag) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PreconditionPanic) +#endif + +#ifndef FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION +#define FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION(condition, flag) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PostconditionPanic) +#endif + +#define PANIC_PRECONDITION_IMPL(cond, format, ...) \ + ::utils::PreconditionPanic::panic(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__) + +#define PANIC_POSTCONDITION_IMPL(cond, format, ...) \ + ::utils::PostconditionPanic::panic(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__) + +#define PANIC_ARITHMETIC_IMPL(cond, format, ...) \ + ::utils::ArithmeticPanic::panic(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__) + +#define PANIC_LOG_IMPL(cond, format, ...) \ + ::utils::details::panicLog(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) + /** * PANIC_PRECONDITION is a macro that reports a PreconditionPanic * @param format printf-style string describing the error in more details */ -#define PANIC_PRECONDITION(format, ...) \ - ::utils::PreconditionPanic::panic(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_PRECONDITION(format, ...) PANIC_PRECONDITION_IMPL(format, format, ##__VA_ARGS__) /** * PANIC_POSTCONDITION is a macro that reports a PostconditionPanic * @param format printf-style string describing the error in more details */ -#define PANIC_POSTCONDITION(format, ...) \ - ::utils::PostconditionPanic::panic(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_POSTCONDITION(format, ...) PANIC_POSTCONDITION_IMPL(format, format, ##__VA_ARGS__) /** * PANIC_ARITHMETIC is a macro that reports a ArithmeticPanic * @param format printf-style string describing the error in more details */ -#define PANIC_ARITHMETIC(format, ...) \ - ::utils::ArithmeticPanic::panic(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_ARITHMETIC(format, ...) PANIC_ARITHMETIC_IMPL(format, format, ##__VA_ARGS__) /** * PANIC_LOG is a macro that logs a Panic, and continues as usual. * @param format printf-style string describing the error in more details */ -#define PANIC_LOG(format, ...) \ - ::utils::details::panicLog(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_LOG(format, ...) PANIC_LOG_IMPL(format, format, ##__VA_ARGS__) /** * @ingroup errors @@ -501,14 +667,14 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @param format printf-style string describing the error in more details */ #define ASSERT_PRECONDITION(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__) : (void)0) + (!UTILS_VERY_LIKELY(cond) ? PANIC_PRECONDITION_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) #define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_PRECONDITION_IMPL(cond, format, ##__VA_ARGS__), false : true) #else #define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__), false : true) #endif @@ -529,14 +695,14 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @endcode */ #define ASSERT_POSTCONDITION(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_POSTCONDITION(format, ##__VA_ARGS__) : (void)0) + (!UTILS_VERY_LIKELY(cond) ? PANIC_POSTCONDITION_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) #define ASSERT_POSTCONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_POSTCONDITION(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_POSTCONDITION_IMPL(cond, format, ##__VA_ARGS__), false : true) #else #define ASSERT_POSTCONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__), false : true) #endif /** @@ -557,14 +723,14 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @endcode */ #define ASSERT_ARITHMETIC(cond, format, ...) \ - (!(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__) : (void)0) + (!(cond) ? PANIC_ARITHMETIC_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) #define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_ARITHMETIC_IMPL(cond, format, ##__VA_ARGS__), false : true) #else #define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__), false : true) #endif /** @@ -588,6 +754,7 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * } * @endcode */ -#define ASSERT_DESTRUCTOR(cond, format, ...) (!(cond) ? PANIC_LOG(format, ##__VA_ARGS__) : (void)0) +#define ASSERT_DESTRUCTOR(cond, format, ...) \ + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #endif // TNT_UTILS_PANIC_H diff --git a/package/android/libs/filament/include/utils/RefCountedMap.h b/package/android/libs/filament/include/utils/RefCountedMap.h new file mode 100644 index 00000000..5fc0b85f --- /dev/null +++ b/package/android/libs/filament/include/utils/RefCountedMap.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2025 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_REFCOUNTEDMAP_H +#define TNT_UTILS_REFCOUNTEDMAP_H + +#include +#include +#include + +#include + +#include +#include +#include + +namespace utils { + +namespace refcountedmap { + +template +struct is_pointer_like_trait : std::false_type {}; + +template +struct is_pointer_like_trait())>> : std::true_type {}; + +template +inline constexpr bool IsPointer = is_pointer_like_trait::value; + +template +struct PointerTraits { + using element_type = T; +}; + +template +struct PointerTraits>> { + using element_type = typename std::pointer_traits::element_type; +}; + +template +struct DefaultValue { + T operator()() const noexcept { + return {}; + } +}; + +} // namespace refcountedmap + +/** A reference-counted map. + * + * Don't use RAII here, both because we sometimes want to deliberately leak memory, and because + * we're managing GL resources that require more managed destruction. + */ +template, + typename NullValue = refcountedmap::DefaultValue> +class RefCountedMap { + // Use references for the key if the size of the key type is greater than the size of a pointer. + using KeyRef = std::conditional_t<(sizeof(Key) > sizeof(void*)), const Key&, Key>; + using TValue = typename refcountedmap::PointerTraits::element_type; + + struct Entry { + uint32_t referenceCount; + T value; + }; + + using Map = tsl::robin_map; + + static constexpr TValue& deref(T& a) { + if constexpr (refcountedmap::IsPointer) { + return *a; + } else { + return a; + } + } + + static constexpr TValue const& deref(T const& a) { + if constexpr (refcountedmap::IsPointer) { + return *a; + } else { + return a; + } + } + + static constexpr const char* UTILS_NONNULL MISSING_ENTRY_ERROR_STRING = + "Cache is missing entry"; + static constexpr const char* UTILS_NONNULL MISSING_VALUE_ERROR_STRING = + "Attempted to get missing value"; + +public: + /** Acquire and return a value by key, initializing it with F if it doesn't exist. + * + * If F returns NullValue{}(), this indicates a failure to create the object. If T is a value + * type, the returned pointer is valid only as long as the next call to acquire() or release(). + */ + template + TValue* UTILS_NULLABLE acquire(KeyRef key, size_t hash, F factory) noexcept { + auto it = mMap.find(key, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return &deref(it.value().value); + } + T r = factory(); + if (r == NullValue{}()) { + return nullptr; + } + // TODO: how to use above computed hash here? + return &deref(mMap.insert({ key, Entry{ 1, std::move(r) } }).first.value().value); + } + + template + inline TValue* UTILS_NULLABLE acquire(KeyRef key, F factory) noexcept { + return acquire(key, Hash{}(key), std::move(factory)); + } + + /** Acquire and return a pointer to the value if one exists. + * + * It's possible to acquire a key before its value is initialized, in which case this function + * returns nullptr. + * + * If T is a value type, this pointer is valid only as long as the next call to acquire() or + * release(). + */ + TValue* UTILS_NULLABLE acquire(KeyRef key, size_t hash) noexcept { + auto it = mMap.find(key, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return &deref(it.value().value); + } + // TODO: how to use above computed hash here? + mMap.insert({ key, Entry{ 1, NullValue{}() } }); + return nullptr; + } + + inline TValue* UTILS_NULLABLE acquire(KeyRef key) noexcept { + return acquire(key, Hash{}(key)); + } + + /** Release a reference to key, destroying it with F if reference count reaches zero. + * + * Panics if no entry found in map. + */ + template + void release(KeyRef key, size_t hash, F releaser) { + auto it = mMap.find(key, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + if (--it.value().referenceCount == 0) { + if (it.value().value != NullValue{}()){ + releaser(deref(it.value().value)); + } + // TODO: change to erase_fast + mMap.erase(it); + } + } + + template + inline void release(KeyRef key, F releaser) noexcept { + release(key, Hash{}(key), std::move(releaser)); + } + + /** Release a reference to key. + * + * Panics if no entry found in map. + */ + void release(KeyRef key, size_t hash) { + auto it = mMap.find(key, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + if (--it.value().referenceCount == 0) { + // TODO: change to erase_fast + mMap.erase(it); + } + } + + inline void release(KeyRef key) noexcept { + release(key, Hash{}(key)); + } + + /** Get a value by key, initializing it with F if it doesn't exist. + * + * If F returns NullValue{}(), this indicates a failure to create the object. If T is a value + * type, the returned pointer is valid only as long as the next call to acquire() or release(). + */ + template + TValue* UTILS_NULLABLE get(KeyRef key, size_t hash, F factory) { + auto it = mMap.find(key, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + const T nullValue = NullValue{}(); + if (it.value().value == nullValue) { + it.value().value = factory(); + if (it.value().value == nullValue) { + return nullptr; + } + } + return &deref(it.value().value); + } + + template + inline TValue* UTILS_NULLABLE get(KeyRef key, F factory) noexcept { + return get(key, Hash{}(key), std::move(factory)); + } + + /** Return reference to existing value by key. + * + * This reference is valid only as long as the next call to acquire() or release(). + * + * Panics if no entry found in map. + */ + TValue& get(KeyRef key, size_t hash) { + auto it = mMap.find(key); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + FILAMENT_CHECK_PRECONDITION(it.value().value != NullValue{}()) + << MISSING_VALUE_ERROR_STRING; + return deref(it.value().value); + } + + inline TValue& get(KeyRef key) noexcept { return get(key, Hash{}(key)); } + + TValue const& get(KeyRef key, size_t hash) const { + auto it = mMap.find(key); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + FILAMENT_CHECK_PRECONDITION(it.value().value != NullValue{}()) + << MISSING_VALUE_ERROR_STRING; + return deref(it->second.value); + } + + inline TValue const& get(KeyRef key) const noexcept { return get(key, Hash{}(key)); } + + /** Returns true if the map is empty. */ + inline bool empty() const noexcept { return mMap.empty(); } + +private: + Map mMap; +}; + +} + +#endif // TNT_UTILS_REFCOUNTEDMAP_H diff --git a/package/android/libs/filament/include/utils/SingleInstanceComponentManager.h b/package/android/libs/filament/include/utils/SingleInstanceComponentManager.h index ddd538f5..fc820ca2 100644 --- a/package/android/libs/filament/include/utils/SingleInstanceComponentManager.h +++ b/package/android/libs/filament/include/utils/SingleInstanceComponentManager.h @@ -108,7 +108,7 @@ class UTILS_PUBLIC SingleInstanceComponentManager { return getComponentCount() == 0; } - utils::Entity const* getEntities() const noexcept { + Entity const* getEntities() const noexcept { return data() + 1; } @@ -238,11 +238,11 @@ class UTILS_PUBLIC SingleInstanceComponentManager { default_random_engine& rng = mRng; UTILS_NOUNROLL while (count && aliveInARow < ratio) { - assert_invariant(count == getComponentCount()); + assert(count == getComponentCount()); // note: using the modulo favorizes lower number size_t const i = rng() % count; Entity const entity = pEntities[i]; - assert_invariant(entity); + assert(entity); if (UTILS_LIKELY(em.isAlive(entity))) { ++aliveInARow; continue; diff --git a/package/android/libs/filament/include/utils/Slice.h b/package/android/libs/filament/include/utils/Slice.h index 444a3b27..59f1043d 100644 --- a/package/android/libs/filament/include/utils/Slice.h +++ b/package/android/libs/filament/include/utils/Slice.h @@ -17,8 +17,11 @@ #ifndef TNT_UTILS_SLICE_H #define TNT_UTILS_SLICE_H +#include #include +#include +#include #include #include @@ -26,116 +29,126 @@ namespace utils { -/* - * A fixed-size slice of a container +/** A fixed-size slice of a container. + * + * Analogous to std::span. */ -template +template class Slice { public: + using element_type = T; + using value_type = std::remove_cv_t; + using size_type = size_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; using iterator = T*; using const_iterator = T const*; - using value_type = T; - using reference = T&; - using const_reference = T const&; - using pointer = T*; - using const_pointer = T const*; - using size_type = size_t; - Slice() noexcept = default; + Slice() = default; + Slice(iterator begin, iterator end) noexcept : mBegin(begin), mEnd(end) {} + Slice(pointer begin, size_type count) noexcept : mBegin(begin), mEnd(begin + count) {} - Slice(const_iterator begin, const_iterator end) noexcept - : mBegin(const_cast(begin)), - mEnd(const_cast(end)) { - } + Slice(Slice const& rhs) : mBegin(rhs.begin()), mEnd(rhs.end()) {} - Slice(const_pointer begin, size_type count) noexcept - : mBegin(const_cast(begin)), - mEnd(mBegin + count) { + // If Slice is Slice, define coercive copy constructor from Slice. + template + Slice(std::enable_if_t, Slice const&> rhs) + : mBegin(rhs.begin()), mEnd(rhs.end()) {} + + Slice& operator=(Slice const& rhs) noexcept { + mBegin = rhs.begin(); + mEnd = rhs.end(); + return *this; } - Slice(Slice const& rhs) noexcept = default; - Slice(Slice&& rhs) noexcept = default; - Slice& operator=(Slice const& rhs) noexcept = default; - Slice& operator=(Slice&& rhs) noexcept = default; + // If Slice is Slice, define assignment operator from Slice. + template + Slice& operator=( + std::enable_if_t, Slice const&> rhs) noexcept { + mBegin = rhs.begin(); + mEnd = rhs.end(); + return *this; + } - void set(pointer begin, size_type count) UTILS_RESTRICT noexcept { + void set(pointer begin, size_type count) noexcept { mBegin = begin; mEnd = begin + count; } - void set(iterator begin, iterator end) UTILS_RESTRICT noexcept { + void set(iterator begin, iterator end) noexcept { mBegin = begin; mEnd = end; } - void swap(Slice& rhs) UTILS_RESTRICT noexcept { + void swap(Slice& rhs) noexcept { std::swap(mBegin, rhs.mBegin); std::swap(mEnd, rhs.mEnd); } - void clear() UTILS_RESTRICT noexcept { + void clear() noexcept { mBegin = nullptr; mEnd = nullptr; } + bool operator==(Slice const& rhs) const noexcept { + if (size() != rhs.size()) { + return false; + } + if (mBegin == rhs.cbegin()) { + return true; + } + return std::equal(cbegin(), cend(), rhs.cbegin()); + } + + bool operator==(Slice const& rhs) const noexcept { + return *this == Slice(rhs); + } + // size - size_t size() const UTILS_RESTRICT noexcept { return mEnd - mBegin; } - size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); } - bool empty() const UTILS_RESTRICT noexcept { return size() == 0; } + size_t size() const noexcept { return mEnd - mBegin; } + size_t sizeInBytes() const noexcept { return size() * sizeof(T); } + bool empty() const noexcept { return size() == 0; } // iterators - iterator begin() UTILS_RESTRICT noexcept { return mBegin; } - const_iterator begin() const UTILS_RESTRICT noexcept { return mBegin; } - const_iterator cbegin() const UTILS_RESTRICT noexcept { return this->begin(); } - iterator end() UTILS_RESTRICT noexcept { return mEnd; } - const_iterator end() const UTILS_RESTRICT noexcept { return mEnd; } - const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); } + iterator begin() const noexcept { return mBegin; } + const_iterator cbegin() const noexcept { return this->begin(); } + iterator end() const noexcept { return mEnd; } + const_iterator cend() const noexcept { return this->end(); } // data access - reference operator[](size_t n) UTILS_RESTRICT noexcept { + reference operator[](size_t n) const noexcept { assert(n < size()); return mBegin[n]; } - const_reference operator[](size_t n) const UTILS_RESTRICT noexcept { - assert(n < size()); - return mBegin[n]; - } - - reference at(size_t n) UTILS_RESTRICT noexcept { + reference at(size_t n) const noexcept { return operator[](n); } - const_reference at(size_t n) const UTILS_RESTRICT noexcept { - return operator[](n); - } - - reference front() UTILS_RESTRICT noexcept { + reference front() const noexcept { assert(!empty()); return *mBegin; } - const_reference front() const UTILS_RESTRICT noexcept { - assert(!empty()); - return *mBegin; - } - - reference back() UTILS_RESTRICT noexcept { + reference back() const noexcept { assert(!empty()); return *(this->end() - 1); } - const_reference back() const UTILS_RESTRICT noexcept { - assert(!empty()); - return *(this->end() - 1); - } - - pointer data() UTILS_RESTRICT noexcept { + pointer data() const noexcept { return this->begin(); } - const_pointer data() const UTILS_RESTRICT noexcept { - return this->begin(); + template> + size_t hash() const noexcept { + Hash hasher; + size_t seed = size(); + for (auto const& it : *this) { + utils::hash::combine_fast(seed, hasher(it)); + } + return seed; } protected: @@ -145,4 +158,13 @@ class Slice { } // namespace utils +namespace std { + +template +struct hash> { + inline size_t operator()(utils::Slice const& lhs) const noexcept { return lhs.hash(); } +}; + +} // namespace std + #endif // TNT_UTILS_SLICE_H diff --git a/package/android/libs/filament/include/utils/StaticString.h b/package/android/libs/filament/include/utils/StaticString.h new file mode 100644 index 00000000..ae2008cb --- /dev/null +++ b/package/android/libs/filament/include/utils/StaticString.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2025 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_STATICSTRING_H +#define TNT_UTILS_STATICSTRING_H + +#include + +#include + +#include + +namespace utils { + +class ImmutableCString; + +/** + * @brief A lightweight string class that stores a pointer to a string literal and its size, without dynamic allocation. + * + * This class is designed to efficiently represent string literals. It does not allocate any memory + * and instead relies on the compiler to manage the memory of the string literals. + */ +class UTILS_PUBLIC StaticString { +public: + using value_type = std::string_view::value_type; + using const_pointer = std::string_view::const_pointer; + using const_reference = std::string_view::const_reference; + using size_type = std::string_view::size_type; + using const_iterator = std::string_view::const_iterator; + + // Constructor from string literal + template + constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor) + + constexpr StaticString() noexcept : mString("", 0) {} + + constexpr const_pointer c_str() const noexcept { return mString.data(); } + constexpr const_pointer data() const noexcept { return mString.data(); } + constexpr size_type size() const noexcept { return mString.size(); } + constexpr size_type length() const noexcept { return mString.size(); } + constexpr bool empty() const noexcept { return mString.empty(); } + + constexpr const_iterator begin() const noexcept { return mString.begin(); } + constexpr const_iterator end() const noexcept { return mString.end(); } + constexpr const_iterator cbegin() const noexcept { return mString.begin(); } + constexpr const_iterator cend() const noexcept { return mString.end(); } + + constexpr const_reference operator[](size_type const pos) const noexcept { + return mString[pos]; + } + + constexpr const_reference at(size_type const pos) const { + return mString[pos]; + } + + constexpr const_reference front() const noexcept { + return mString.front(); + } + + constexpr const_reference back() const noexcept { + return mString.back(); + } + + constexpr int compare(const StaticString& rhs) const noexcept { + return mString.compare(rhs.mString); + } + +private: +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs); +#endif + + std::string_view mString; + + friend constexpr bool operator==(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString == rhs.mString; + } + + friend constexpr bool operator!=(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString != rhs.mString; + } + + friend constexpr bool operator<(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString < rhs.mString; + } + + friend constexpr bool operator>(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString > rhs.mString; + } + + friend constexpr bool operator<=(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString <= rhs.mString; + } + + friend constexpr bool operator>=(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString >= rhs.mString; + } +}; + +} // namespace utils + +#endif // TNT_UTILS_STATICSTRING_H diff --git a/package/android/libs/filament/include/utils/Status.h b/package/android/libs/filament/include/utils/Status.h new file mode 100644 index 00000000..22a8eb4f --- /dev/null +++ b/package/android/libs/filament/include/utils/Status.h @@ -0,0 +1,162 @@ +/* +* Copyright (C) 2025 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_STATUS_H +#define TNT_UTILS_STATUS_H + +#include +#include +#include +#include + +namespace utils { + +/** + * A code indicating the success or failure of an operation. + */ +enum class StatusCode { + /** The operation completed successfully. */ + OK, + /** The caller provided invalid arguments in the request. */ + INVALID_ARGUMENT, + /** Internal error was occurred while processing the request. */ + INTERNAL, + /** The requested operation is not supported. */ + UNSUPPORTED, +}; + +/** + * Returns the StatusCode to indicate whether the request was successful. + * If successful, it returns OK with an optional message, if not it returns + * other codes with an optional error message. + */ +class UTILS_PUBLIC Status { +public: + /** + * Creates a new Status with a StatusCode of OK. + */ + Status() : mStatusCode(StatusCode::OK) {} + + /** + * Creates a new Status with the given status code and supplementary message. + * + * @param statusCode The status code to use. + * @param message An optional message, usually contains the reason for the failure. + */ + Status(StatusCode statusCode, std::string_view message) : + mStatusCode(statusCode), + mMessage(message.data(), message.length()) {} + + Status(const Status& other) = default; + + Status(Status&& other) noexcept = default; + + ~Status() = default; + + Status& operator=(const Status& other) = default; + Status& operator=(Status&& other) noexcept = default; + + bool operator==(const Status& other) const { + return mStatusCode == other.mStatusCode && mMessage == other.mMessage; + } + + bool operator!=(const Status& other) const { + return !(*this == other); + } + + /** + * Returns true if the status is OK. + * @return true if the operation was successful, false otherwise. + */ + bool isOk() const { + return mStatusCode == StatusCode::OK; + } + + /** + * Returns the StatusCode for this Status. + * @return the StatusCode for this Status. + */ + StatusCode getCode() const { + return mStatusCode; + } + + /** + * Returns the message for this Status. + * @return The message string. Can be empty if it's not set. + */ + std::string_view getMessage() const { + return std::string_view(mMessage.begin(), mMessage.length()); + } + + /** + * Convenient factory functions for creating Status objects. + * Example usage: `return utils::Status::internal("internal error");` + */ + + /** + * Creates a success Status with a StatusCode of OK. + * @return a success Status with a StatusCode of OK + */ + static Status ok() { + return {}; + } + + /** + * Creates a success Status with a StatusCode of OK with a supplementary message. + * @return a success Status with a StatusCode of OK with a supplementary message. + */ + static Status ok(std::string_view message) { + return {StatusCode::OK, message}; + } + + /** + * Creates an error Status with an INTERNAL status code. + * @param message The error message to include. + * @return an error Status with an INTERNAL status code. + */ + static Status internal(std::string_view message) { + return {StatusCode::INTERNAL, message}; + } + + /** + * Creates an error Status with an INVALID_ARGUMENT status code. + * @param message The error message to include. + * @return an error Status with an INVALID_ARGUMENT status code. + */ + static Status invalidArgument(std::string_view message) { + return {StatusCode::INVALID_ARGUMENT, message}; + } + + /** + * Creates an error Status with an UNSUPPORTED status code. + * @param message The error message to include. + * @return an error Status with an UNSUPPORTED status code. + */ + static Status unsupported(std::string_view message) { + return { StatusCode::UNSUPPORTED, message }; + } + +private: + StatusCode mStatusCode; + // Additional message for the Status. Usually contains the reason for the error. + utils::CString mMessage; +}; + +utils::io::ostream& operator<<(utils::io::ostream& os, const Status& status); + +} // namespace utils + +#endif // TNT_UTILS_STATUS_H diff --git a/package/android/libs/filament/include/utils/StructureOfArrays.h b/package/android/libs/filament/include/utils/StructureOfArrays.h index a4309584..923744be 100644 --- a/package/android/libs/filament/include/utils/StructureOfArrays.h +++ b/package/android/libs/filament/include/utils/StructureOfArrays.h @@ -20,14 +20,8 @@ #include #include #include -#include #include -#include -#include -#include -#include - #include #include // note: this is safe, see how std::array is used below (inline / private) #include @@ -35,6 +29,12 @@ #include #include +#include +#include +#include +#include +#include + namespace utils { template @@ -130,7 +130,7 @@ class StructureOfArraysBase { friend class IteratorValueRef; friend iterator; friend const_iterator; - using Type = std::tuple::type...>; + using Type = std::tuple...>; Type elements; template @@ -368,7 +368,7 @@ class StructureOfArraysBase { size_t last = mSize++; // Fold expression on the comma operator ([&]{ - new(std::get(mArrays) + last) Elements{std::get(args)}; + new(std::get(mArrays) + last) Elements{std::get(std::forward(args))}; }() , ...); } @@ -513,7 +513,7 @@ class StructureOfArraysBase { return (soa.elementAt(i) = other); } UTILS_ALWAYS_INLINE Type const& operator = (Type&& other) noexcept { - return (soa.elementAt(i) = other); + return (soa.elementAt(i) = std::forward(other)); } // comparisons UTILS_ALWAYS_INLINE bool operator==(Type const& other) const { @@ -535,29 +535,29 @@ class StructureOfArraysBase { private: template - inline typename std::enable_if::type + inline std::enable_if_t for_each(std::tuple&, FuncT) {} template - inline typename std::enable_if::type + inline std::enable_if_t for_each(std::tuple& t, FuncT f) { f(I, std::get(t)); for_each(t, f); } template - inline typename std::enable_if::type + inline std::enable_if_t for_each_index(std::tuple&, FuncT) {} template - inline typename std::enable_if::type + inline std::enable_if_t for_each_index(std::tuple& t, FuncT f) { f.template operator()(std::get(t)); for_each_index(t, f); } inline void resizeNoCheck(size_t needed) noexcept { - assert_invariant(mCapacity >= needed); + assert(mCapacity >= needed); if (needed < mSize) { // we shrink the arrays destroy_each(needed, mSize); @@ -590,14 +590,14 @@ class StructureOfArraysBase { size_t unalignment = (offsets[i - 1] + sizes[i - 1]) % alignments[i]; size_t alignment = unalignment ? (alignments[i] - unalignment) : 0; offsets[i] = offsets[i - 1] + (sizes[i - 1] + alignment); - assert_invariant(offsets[i] % alignments[i] == 0); + assert(offsets[i] % alignments[i] == 0); } return offsets; } void construct_each(size_t from, size_t to) noexcept { forEach([from, to](auto p) { - using T = typename std::decay::type; + using T = std::decay_t; // note: scalar types like int/float get initialized to zero if constexpr (!std::is_trivially_default_constructible_v) { for (size_t i = from; i < to; i++) { @@ -609,7 +609,7 @@ class StructureOfArraysBase { void destroy_each(size_t from, size_t to) noexcept { forEach([from, to](auto p) { - using T = typename std::decay::type; + using T = std::decay_t; if constexpr (!std::is_trivially_destructible_v) { for (size_t i = from; i < to; i++) { p[i].~T(); @@ -624,7 +624,7 @@ class StructureOfArraysBase { if (mSize) { auto size = mSize; // placate a compiler warning forEach([buffer, &index, &offsets, size](auto p) { - using T = typename std::decay::type; + using T = std::decay_t; T* UTILS_RESTRICT b = static_cast(buffer); // go through each element and move them from the old array to the new @@ -671,7 +671,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::operator=( - StructureOfArraysBase::IteratorValueRef const& rhs) { + IteratorValueRef const& rhs) { return operator=(IteratorValue(rhs)); } @@ -679,7 +679,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::operator=( - StructureOfArraysBase::IteratorValueRef&& rhs) noexcept { + IteratorValueRef&& rhs) noexcept { return operator=(IteratorValue(rhs)); } @@ -688,7 +688,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::assign( - StructureOfArraysBase::IteratorValue const& rhs, std::index_sequence) { + IteratorValue const& rhs, std::index_sequence) { // implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue const& rhs) auto UTILS_UNUSED l = { (soa->elementAt(index) = std::get(rhs.elements), 0)... }; return *this; @@ -699,7 +699,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::assign( - StructureOfArraysBase::IteratorValue&& rhs, std::index_sequence) noexcept { + IteratorValue&& rhs, std::index_sequence) noexcept { // implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue&& rhs) noexcept auto UTILS_UNUSED l = { (soa->elementAt(index) = std::move(std::get(rhs.elements)), 0)... }; diff --git a/package/android/libs/filament/include/utils/Systrace.h b/package/android/libs/filament/include/utils/Systrace.h index 9f5a7f2a..cc99ad39 100644 --- a/package/android/libs/filament/include/utils/Systrace.h +++ b/package/android/libs/filament/include/utils/Systrace.h @@ -14,10 +14,16 @@ * limitations under the License. */ +/*********************************************************************** + * DEPRECATED * + * * + * Systrace.h is deprecated as a public API. There is no replacement. * + * Applications should instead use the Perfetto SDK directly. * + ***********************************************************************/ + #ifndef TNT_UTILS_SYSTRACE_H #define TNT_UTILS_SYSTRACE_H - #define SYSTRACE_TAG_NEVER (0) #define SYSTRACE_TAG_ALWAYS (1<<0) #define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles diff --git a/package/android/libs/filament/include/utils/algorithm.h b/package/android/libs/filament/include/utils/algorithm.h index ea5ca44f..c4f400f4 100644 --- a/package/android/libs/filament/include/utils/algorithm.h +++ b/package/android/libs/filament/include/utils/algorithm.h @@ -22,6 +22,7 @@ #include // for std::enable_if #include +#include #include namespace utils { @@ -38,14 +39,20 @@ constexpr inline T popcount(T v) noexcept { return (T) (v * (ONES / 255)) >> (sizeof(T) - 1) * CHAR_BIT; } -template::value>> +template>> constexpr inline T clz(T x) noexcept { static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits"); x |= (x >> 1u); x |= (x >> 2u); - x |= (x >> 4u); - x |= (x >> 8u); - x |= (x >> 16u); + if constexpr (sizeof(T) * CHAR_BIT >= 8) { // just to silence compiler warning + x |= (x >> 4u); + } + if constexpr (sizeof(T) * CHAR_BIT >= 16) { // just to silence compiler warning + x |= (x >> 8u); + } + if constexpr (sizeof(T) * CHAR_BIT >= 32) { // just to silence compiler warning + x |= (x >> 16u); + } if constexpr (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning x |= (x >> 32u); } @@ -55,7 +62,7 @@ constexpr inline T clz(T x) noexcept { return T(sizeof(T) * CHAR_BIT) - details::popcount(x); } -template::value>> +template>> constexpr inline T ctz(T x) noexcept { static_assert(sizeof(T) * CHAR_BIT <= 64, "details::ctz() only support up to 64 bits"); T c = sizeof(T) * CHAR_BIT; @@ -67,11 +74,15 @@ constexpr inline T ctz(T x) noexcept { x &= -x; #endif if (x) c--; - if (sizeof(T) * CHAR_BIT >= 64) { + if constexpr (sizeof(T) * CHAR_BIT >= 64) { if (x & T(0x00000000FFFFFFFF)) c -= 32; } - if (x & T(0x0000FFFF0000FFFF)) c -= 16; - if (x & T(0x00FF00FF00FF00FF)) c -= 8; + if constexpr (sizeof(T) * CHAR_BIT >= 32) { + if (x & T(0x0000FFFF0000FFFF)) c -= 16; + } + if constexpr (sizeof(T) * CHAR_BIT >= 16) { + if (x & T(0x00FF00FF00FF00FF)) c -= 8; + } if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4; if (x & T(0x3333333333333333)) c -= 2; if (x & T(0x5555555555555555)) c -= 1; @@ -80,6 +91,24 @@ constexpr inline T ctz(T x) noexcept { } // namespace details +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned char x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz((unsigned int)x) - 24; +#else + return details::clz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned short x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz((unsigned int)x) - 16; +#else + return details::clz(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept { #if __has_builtin(__builtin_clz) @@ -107,6 +136,24 @@ unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept { #endif } +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned char x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned short x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept { #if __has_builtin(__builtin_ctz) @@ -134,6 +181,24 @@ unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept { #endif } +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned char x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned short x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept { #if __has_builtin(__builtin_popcount) @@ -161,13 +226,8 @@ unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept { #endif } -constexpr inline UTILS_PUBLIC UTILS_PURE -uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept { - return (uint8_t)popcount((unsigned int)x); -} - template::value && std::is_unsigned::value>> + typename = std::enable_if_t && std::is_unsigned_v>> constexpr inline UTILS_PUBLIC UTILS_PURE T log2i(T x) noexcept { return (sizeof(x) * 8 - 1u) - clz(x); diff --git a/package/android/libs/filament/include/utils/bitset.h b/package/android/libs/filament/include/utils/bitset.h index 281e5dfc..314ad2b8 100644 --- a/package/android/libs/filament/include/utils/bitset.h +++ b/package/android/libs/filament/include/utils/bitset.h @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -27,6 +26,7 @@ #include // for std::fill #include +#include #include #if defined(__ARM_NEON) @@ -45,8 +45,8 @@ namespace utils { */ template::value && - std::is_unsigned::value>::type> + typename = std::enable_if_t && + std::is_unsigned_v>> class UTILS_PUBLIC bitset { T storage[N]; @@ -60,13 +60,18 @@ class UTILS_PUBLIC bitset { std::fill(std::begin(storage), std::end(storage), 0); } + template> + explicit bitset(U value) noexcept { + storage[0] = value; + } + T getBitsAt(size_t n) const noexcept { - assert_invariant(n::max(); + } + size_t size() const noexcept { return N * BITS_PER_WORD; } + bool empty() const noexcept { return none(); } + bool test(size_t bit) const noexcept { return operator[](bit); } void set(size_t b) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] |= T(1) << (b % BITS_PER_WORD); } void set(size_t b, bool value) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD)); storage[b / BITS_PER_WORD] |= T(value) << (b % BITS_PER_WORD); } void unset(size_t b) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD)); } void flip(size_t b) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD); } - void reset() noexcept { std::fill(std::begin(storage), std::end(storage), 0); } + void clear() noexcept { + reset(); + } + bool operator[](size_t b) const noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD))); } diff --git a/package/android/libs/filament/include/utils/compiler.h b/package/android/libs/filament/include/utils/compiler.h index 710c901e..79ee3dda 100644 --- a/package/android/libs/filament/include/utils/compiler.h +++ b/package/android/libs/filament/include/utils/compiler.h @@ -33,7 +33,7 @@ #if __has_attribute(visibility) # define UTILS_PUBLIC __attribute__((visibility("default"))) #else -# define UTILS_PUBLIC +# define UTILS_PUBLIC #endif #if __has_attribute(deprecated) @@ -104,6 +104,19 @@ # define UTILS_UNLIKELY( exp ) (!!(exp)) #endif +#if __has_builtin(__builtin_expect_with_probability) +# ifdef __cplusplus +# define UTILS_VERY_LIKELY( exp ) (__builtin_expect_with_probability( !!(exp), true, 0.995 )) +# define UTILS_VERY_UNLIKELY( exp ) (__builtin_expect_with_probability( !!(exp), false, 0.995 )) +# else +# define UTILS_VERY_LIKELY( exp ) (__builtin_expect_with_probability( !!(exp), 1, 0.995 )) +# define UTILS_VERY_UNLIKELY( exp ) (__builtin_expect_with_probability( !!(exp), 0, 0.995 )) +# endif +#else +# define UTILS_VERY_LIKELY( exp ) (!!(exp)) +# define UTILS_VERY_UNLIKELY( exp ) (!!(exp)) +#endif + #if __has_builtin(__builtin_prefetch) # define UTILS_PREFETCH( exp ) (__builtin_prefetch(exp)) #else @@ -251,7 +264,7 @@ typedef SSIZE_T ssize_t; #if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__) # define __PRETTY_FUNCTION__ __FUNCSIG__ -#endif +#endif #if defined(_MSC_VER) diff --git a/package/android/libs/filament/include/utils/debug.h b/package/android/libs/filament/include/utils/debug.h index 0f6ecdb2..7587c12f 100644 --- a/package/android/libs/filament/include/utils/debug.h +++ b/package/android/libs/filament/include/utils/debug.h @@ -28,7 +28,7 @@ void panic(const char *func, const char * file, int line, const char *assertion) # define assert_invariant(e) ((void)0) #else # define assert_invariant(e) \ - (UTILS_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e)) + (UTILS_VERY_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e)) #endif // NDEBUG #endif // TNT_UTILS_DEBUG_H diff --git a/package/android/libs/filament/include/utils/memalign.h b/package/android/libs/filament/include/utils/memalign.h index 4c048bff..a7043310 100644 --- a/package/android/libs/filament/include/utils/memalign.h +++ b/package/android/libs/filament/include/utils/memalign.h @@ -32,7 +32,7 @@ namespace utils { inline void* aligned_alloc(size_t size, size_t align) noexcept { // 'align' must be a power of two and a multiple of sizeof(void*) align = (align < sizeof(void*)) ? sizeof(void*) : align; - assert(align && !(align & align - 1)); + assert(align && !(align & (align - 1))); assert((align % sizeof(void*)) == 0); void* p = nullptr; @@ -40,7 +40,7 @@ inline void* aligned_alloc(size_t size, size_t align) noexcept { #if defined(WIN32) p = ::_aligned_malloc(size, align); #else - ::posix_memalign(&p, align, size); + (void) ::posix_memalign(&p, align, size); #endif return p; } @@ -73,8 +73,8 @@ class STLAlignedAllocator { using const_pointer = const TYPE*; using reference = TYPE&; using const_reference = const TYPE&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; + using size_type = ::size_t; + using difference_type = ::ptrdiff_t; using propagate_on_container_move_assignment = std::true_type; using is_always_equal = std::true_type; diff --git a/package/android/libs/filament/include/utils/ostream.h b/package/android/libs/filament/include/utils/ostream.h index cde8e75b..ea4952cc 100644 --- a/package/android/libs/filament/include/utils/ostream.h +++ b/package/android/libs/filament/include/utils/ostream.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,7 @@ namespace utils::io { struct ostream_; -class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { +class UTILS_PUBLIC ostream : protected PrivateImplementation { friend struct ostream_; public: @@ -95,11 +96,11 @@ class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { size_t length() const noexcept; private: - void reserve(size_t newSize) noexcept; + void reserve(size_t newCapacity) noexcept; char* buffer = nullptr; // buffer address char* curr = nullptr; // current pointer - size_t size = 0; // size remaining + size_t sizeRemaining = 0; // size remaining size_t capacity = 0; // total capacity of the buffer }; @@ -122,11 +123,6 @@ class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { const char* getFormat(type t) const noexcept; }; -// handles utils::bitset -inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept { - return o << (void*)uintptr_t(s.getValue()); -} - // handles vectors from libmath (but we do this generically, without needing a dependency on libmath) template class VECTOR, typename T> inline ostream& operator<<(ostream& stream, const VECTOR& v) { @@ -144,4 +140,21 @@ inline ostream& endl(ostream& s) noexcept { return flush(s << '\n'); } } // namespace utils::io +namespace utils { + +// handles utils::bitset + +namespace io { +class ostream; +} + +inline std::ostream& operator<<(std::ostream& o, bitset32 const& s) noexcept { + return o << (void*) uintptr_t(s.getValue()); +} +inline io::ostream& operator<<(io::ostream& o, bitset32 const& s) noexcept { + return o << (void*) uintptr_t(s.getValue()); +} +}// namespace utils + + #endif // TNT_UTILS_OSTREAM_H diff --git a/package/android/libs/filament/include/utils/sstream.h b/package/android/libs/filament/include/utils/sstream.h new file mode 100644 index 00000000..58d14fef --- /dev/null +++ b/package/android/libs/filament/include/utils/sstream.h @@ -0,0 +1,36 @@ +/* + * 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_SSTREAM_H +#define TNT_UTILS_SSTREAM_H + +#include +#include + +#include + +namespace utils::io { + +class UTILS_PUBLIC sstream : public ostream { +public: + ostream& flush() noexcept override; + const char* c_str() const noexcept; + size_t length() const noexcept; +}; + +} // namespace utils::io + +#endif // TNT_UTILS_SSTREAM_H diff --git a/package/android/libs/filament/include/viewer/AutomationEngine.h b/package/android/libs/filament/include/viewer/AutomationEngine.h index 8747f59d..3c7257f2 100644 --- a/package/android/libs/filament/include/viewer/AutomationEngine.h +++ b/package/android/libs/filament/include/viewer/AutomationEngine.h @@ -56,6 +56,22 @@ class UTILS_PUBLIC AutomationEngine { * Allows users to toggle screenshots, change the sleep duration between tests, etc. */ struct Options { + + /** + * Formats that could be used for exporting the screenshots. + */ + enum class ExportFormat : uint8_t { + /** + * Tagged Image File Format (TIFF) + */ + TIFF = 0, + + /** + * Netpbm color image format (Portable Pixel Map) + */ + PPM = 1, + }; + /** * Minimum time that automation waits between applying a settings object and advancing * to the next test case. Specified in seconds. @@ -82,6 +98,11 @@ class UTILS_PUBLIC AutomationEngine { * If true, the tick function writes out a settings JSON file before advancing. */ bool exportSettings = false; + + /** + * Which image format will be used for exporting screenshots. + */ + ExportFormat exportFormat = ExportFormat::TIFF; }; /** @@ -224,6 +245,9 @@ class UTILS_PUBLIC AutomationEngine { */ static void exportSettings(const Settings& settings, const char* filename); + static void exportScreenshot(View* view, Renderer* renderer, std::string filename, + bool autoclose, AutomationEngine* automationEngine); + Options getOptions() const { return mOptions; } bool isRunning() const { return mIsRunning; } size_t currentTest() const { return mCurrentTest; } diff --git a/package/android/libs/filament/include/viewer/Settings.h b/package/android/libs/filament/include/viewer/Settings.h index 2094296b..b16ee8ae 100644 --- a/package/android/libs/filament/include/viewer/Settings.h +++ b/package/android/libs/filament/include/viewer/Settings.h @@ -51,6 +51,7 @@ struct Settings; struct ViewSettings; struct LightSettings; struct ViewerOptions; +struct DebugOptions; enum class ToneMapping : uint8_t { LINEAR = 0, @@ -88,6 +89,8 @@ void applySettings(Engine* engine, const LightSettings& settings, IndirectLight* const utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene, View* view); void applySettings(Engine* engine, const ViewerOptions& settings, Camera* camera, Skybox* skybox, Renderer* renderer); +void applySettings(Engine* engine, const DebugOptions& settings, + Renderer* renderer); // Creates a new ColorGrading object based on the given settings. UTILS_PUBLIC @@ -246,11 +249,16 @@ struct ViewerOptions { bool autoInstancingEnabled = false; }; +struct DebugOptions { + uint16_t skipFrames = 0; +}; + struct Settings { ViewSettings view; MaterialSettings material; LightSettings lighting; ViewerOptions viewer; + DebugOptions debug; }; } // namespace viewer diff --git a/package/android/src/main/cpp/RNFFilament.cpp b/package/android/src/main/cpp/RNFFilament.cpp index accdd9b5..5a871a44 100644 --- a/package/android/src/main/cpp/RNFFilament.cpp +++ b/package/android/src/main/cpp/RNFFilament.cpp @@ -7,8 +7,11 @@ #include "RNFJSurfaceProvider.h" #include #include +#include JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { + filament::VirtualMachineEnv::JNI_OnLoad(vm); + return facebook::jni::initialize(vm, [] { margelo::FilamentInstaller::registerNatives(); margelo::JFilamentProxy::registerNatives(); diff --git a/package/cpp/core/RNFEngineConfigHelper.cpp b/package/cpp/core/RNFEngineConfigHelper.cpp index d78e4869..5322b222 100644 --- a/package/cpp/core/RNFEngineConfigHelper.cpp +++ b/package/cpp/core/RNFEngineConfigHelper.cpp @@ -31,8 +31,8 @@ Engine::Config EngineConfigHelper::makeConfigFromUserParams(std::optional(params["jobSystemThreadCount"]); } - if (params.find("textureUseAfterFreePoolSize") != params.end()) { - config.textureUseAfterFreePoolSize = static_cast(params["textureUseAfterFreePoolSize"]); + if (params.find("disableHandleUseAfterFreeCheck") != params.end()) { + config.disableHandleUseAfterFreeCheck = static_cast(params["disableHandleUseAfterFreeCheck"]); } if (params.find("resourceAllocatorCacheMaxAge") != params.end()) { config.resourceAllocatorCacheMaxAge = static_cast(params["resourceAllocatorCacheMaxAge"]); diff --git a/package/filament_animator_feat.patch b/package/filament_animator_feat.patch index 85c239f4..d362023f 100644 --- a/package/filament_animator_feat.patch +++ b/package/filament_animator_feat.patch @@ -1,5 +1,5 @@ diff --git a/libs/gltfio/include/gltfio/Animator.h b/libs/gltfio/include/gltfio/Animator.h -index 199555a40..dd550938c 100644 +index 199555a4..e02a94a6 100644 --- a/libs/gltfio/include/gltfio/Animator.h +++ b/libs/gltfio/include/gltfio/Animator.h @@ -20,6 +20,8 @@ @@ -20,7 +20,7 @@ index 199555a40..dd550938c 100644 + * This is useful if you have other instances that have the same skeleton as the animator + * from this asset, and you want those instances to be animated by the same animation (e.g. clothing). + * Usually you don't need this and using updateBoneMatrices() is enough. -+ * ++ * + * @param instance The instance to update. + */ + void updateBoneMatricesForInstance(FilamentInstance* instance); @@ -29,7 +29,7 @@ index 199555a40..dd550938c 100644 * Applies a blended transform to the union of nodes affected by two animations. * Used for cross-fading from a previous skinning-based animation or rigid body animation. diff --git a/libs/gltfio/src/Animator.cpp b/libs/gltfio/src/Animator.cpp -index 6e53b705c..ff41bdf59 100644 +index 7b1bd6eb..75db038d 100644 --- a/libs/gltfio/src/Animator.cpp +++ b/libs/gltfio/src/Animator.cpp @@ -336,6 +336,10 @@ void Animator::updateBoneMatrices() { diff --git a/package/filament_depth_clip_mode.patch b/package/filament_depth_clip_mode.patch new file mode 100644 index 00000000..776238e4 --- /dev/null +++ b/package/filament_depth_clip_mode.patch @@ -0,0 +1,16 @@ +diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm +index 1234567..abcdefg 100644 +--- a/filament/backend/src/metal/MetalDriver.mm ++++ b/filament/backend/src/metal/MetalDriver.mm +@@ -161,7 +161,10 @@ MetalDriver::MetalDriver(backend::DriverConfig const& driverConfig, + + mContext->supportsDepthClamp = false; + if (@available(macOS 10.11, iOS 11.0, *)) { +- mContext->supportsDepthClamp = true; ++ // Depth clip mode requires GPU family support (not available on older simulators) ++ mContext->supportsDepthClamp = ++ mContext->highestSupportedGpuFamily.apple >= 3 || // Apple A11 and later (iPhone 8+) ++ mContext->highestSupportedGpuFamily.mac >= 1; // All Mac GPUs + } + + // In order to support resolve store action on depth attachment, the GPU needs to support it. diff --git a/package/filament_ios_simulator.patch b/package/filament_ios_simulator.patch index f7eef22e..8af6aaf7 100644 --- a/package/filament_ios_simulator.patch +++ b/package/filament_ios_simulator.patch @@ -1,8 +1,8 @@ diff --git a/CMakeLists.txt b/CMakeLists.txt -index ed32949873a..35c10e6717e 100644 +index a706f439..b3043e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt -@@ -611,7 +611,11 @@ endif() +@@ -696,7 +696,11 @@ endif() string(TOLOWER "${DIST_ARCH}" DIST_ARCH) string(REPLACE "amd64" "x86_64" DIST_ARCH "${DIST_ARCH}") if (NOT DIST_DIR) @@ -15,20 +15,11 @@ index ed32949873a..35c10e6717e 100644 endif() # ================================================================================================== -diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md -index 4a1a9c7fa7e..aa2ee9c76e0 100644 ---- a/NEW_RELEASE_NOTES.md -+++ b/NEW_RELEASE_NOTES.md -@@ -7,3 +7,4 @@ for next branch cut* header. - appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). - - ## Release notes for next branch cut -+- ios: Add support for Apple Silicon (arm64) simulator diff --git a/build.sh b/build.sh -index 56562cc1c6c..3cb660aa1f9 100755 +index ecabc0fb..da9a40f2 100755 --- a/build.sh +++ b/build.sh -@@ -581,9 +581,9 @@ function build_ios_target { +@@ -679,9 +679,9 @@ function build_ios_target { local platform=$3 echo "Building iOS ${lc_target} (${arch}) for ${platform}..." @@ -40,9 +31,9 @@ index 56562cc1c6c..3cb660aa1f9 100755 if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then cmake \ -@@ -598,14 +598,14 @@ function build_ios_target { - ${MATDBG_OPTION} \ +@@ -699,14 +699,14 @@ function build_ios_target { ${MATOPT_OPTION} \ + ${STEREOSCOPIC_OPTION} \ ../.. - ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \ + ln -sf "out/cmake-ios-${lc_target}-${arch}-${platform}/compile_commands.json" \ @@ -57,15 +48,14 @@ index 56562cc1c6c..3cb660aa1f9 100755 ${BUILD_COMMAND} ${INSTALL_COMMAND} fi -@@ -640,16 +640,26 @@ function build_ios { +@@ -741,16 +741,26 @@ function build_ios { if [[ "${ISSUE_DEBUG_BUILD}" == "true" ]]; then build_ios_target "Debug" "arm64" "iphoneos" if [[ "${IOS_BUILD_SIMULATOR}" == "true" ]]; then + build_ios_target "Debug" "arm64" "iphonesimulator" build_ios_target "Debug" "x86_64" "iphonesimulator" fi -- -+ + if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then build/ios/create-universal-libs.sh \ - -o out/ios-debug/filament/lib/universal \ @@ -79,18 +69,18 @@ index 56562cc1c6c..3cb660aa1f9 100755 + + rm -rf out/ios-debug/filament/lib/x86_64-iphonesimulator + rm -rf out/ios-debug/filament/lib/arm64-iphonesimulator -+ ++ + build/ios/create-xc-frameworks.sh \ + -o out/ios-debug/filament/lib \ + out/ios-debug/filament/lib/arm64-iphoneos \ -+ out/ios-debug/filament/lib/universal -+ ++ out/ios-debug/filament/lib/universal ++ + rm -rf out/ios-debug/filament/lib/universal + rm -rf out/ios-debug/filament/lib/arm64-iphoneos fi archive_ios "Debug" -@@ -657,19 +667,39 @@ function build_ios { +@@ -758,17 +768,28 @@ function build_ios { if [[ "${ISSUE_RELEASE_BUILD}" == "true" ]]; then build_ios_target "Release" "arm64" "iphoneos" @@ -98,19 +88,15 @@ index 56562cc1c6c..3cb660aa1f9 100755 if [[ "${IOS_BUILD_SIMULATOR}" == "true" ]]; then + build_ios_target "Release" "arm64" "iphonesimulator" build_ios_target "Release" "x86_64" "iphonesimulator" -- fi + fi -- if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then -+ # Create universal libraries - since simulator and iphone is both arm64 -+ # we can create a universal library containing both platforms + if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then build/ios/create-universal-libs.sh \ - -o out/ios-release/filament/lib/universal \ - out/ios-release/filament/lib/arm64 \ - out/ios-release/filament/lib/x86_64 - rm -rf out/ios-release/filament/lib/arm64 - rm -rf out/ios-release/filament/lib/x86_64 -- fi -- + -o ./out/ios-release/filament/lib/universal \ + ./out/ios-release/filament/lib/arm64-iphonesimulator \ + ./out/ios-release/filament/lib/x86_64-iphonesimulator @@ -118,33 +104,46 @@ index 56562cc1c6c..3cb660aa1f9 100755 + rm -rf out/ios-release/filament/lib/x86_64-iphonesimulator + rm -rf out/ios-release/filament/lib/arm64-iphonesimulator + -+ # Create XC Frameworks + build/ios/create-xc-frameworks.sh \ + -o out/ios-release/filament/lib \ + out/ios-release/filament/lib/arm64-iphoneos \ -+ out/ios-release/filament/lib/universal ++ out/ios-release/filament/lib/universal + + rm -rf out/ios-release/filament/lib/universal + rm -rf out/ios-release/filament/lib/arm64-iphoneos -+ else -+ # Create XC Frameworks for arm64 only - no need to create -+ # universal libraries -+ build/ios/create-xc-frameworks.sh \ -+ -o out/ios-release/filament/lib \ -+ out/ios-release/filament/lib/arm64-iphoneos -+ -+ rm -rf out/ios-release/filament/lib/arm64-iphoneos -+ fi -+ + fi + archive_ios "Release" - fi - } +diff --git a/third_party/clang/iOS.cmake b/third_party/clang/iOS.cmake +index a935c08f..ea491860 100644 +--- a/third_party/clang/iOS.cmake ++++ b/third_party/clang/iOS.cmake +@@ -29,8 +29,17 @@ SET(CMAKE_CXX_COMPILER_WORKS True) + SET(CMAKE_C_COMPILER_WORKS True) + SET(DARWIN_TARGET_OS_NAME ios) + +-SET(PLATFORM_NAME "iphoneos" CACHE STRING "iOS platform to build for") +-SET(PLATFORM_FLAG_NAME ios) ++IF(NOT DEFINED PLATFORM_NAME) ++ SET(PLATFORM_NAME "iphoneos" CACHE STRING "iOS platform to build for") ++ENDIF() ++ ++IF(PLATFORM_NAME STREQUAL "iphonesimulator") ++ SET(PLATFORM_FLAG_NAME ios-simulator) ++ MESSAGE(STATUS "Building for iOS Simulator - PLATFORM_FLAG_NAME=${PLATFORM_FLAG_NAME}") ++ELSE() ++ SET(PLATFORM_FLAG_NAME ios) ++ MESSAGE(STATUS "Building for iOS Device - PLATFORM_FLAG_NAME=${PLATFORM_FLAG_NAME}") ++ENDIF() + + IF("$ENV{RC_APPLETV}" STREQUAL "YES") + MESSAGE(STATUS "Building for tvos") diff --git a/build/ios/create-xc-frameworks.sh b/build/ios/create-xc-frameworks.sh new file mode 100755 -index 00000000000..cf720cb7e4e +index 00000000..0314d09b --- /dev/null +++ b/build/ios/create-xc-frameworks.sh -@@ -0,0 +1,107 @@ +@@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +set -e @@ -152,32 +151,22 @@ index 00000000000..cf720cb7e4e +function print_help { + local SELF_NAME + SELF_NAME=$(basename "$0") -+ echo "$SELF_NAME. Combine multiple single-architecture or universal libraries into xc-frameworks." ++ echo "$SELF_NAME. Create XCFrameworks from device and simulator libraries." + echo "" + echo "Usage:" -+ echo " $SELF_NAME [options] ..." ++ echo " $SELF_NAME [options] " + echo "" + echo "Options:" + echo " -h" + echo " Print this help message." + echo " -o" -+ echo " Output directory to store the xcframeworks libraries." ++ echo " Output directory to store the XCFrameworks." + echo "" + echo "Example:" -+ echo " Given the follow directories:" -+ echo " ├── universal/" -+ echo " │ └── libfoo.a <- universal library - ensure they share the same platform (iphone/simulator)" -+ echo " └── arm64-iphoneos/" -+ echo " └── libfoo.a <- arm64 iphoneos platform" -+ echo "" -+ echo " $SELF_NAME -o frameworks/ arm64-iphoneos/ universal/" -+ echo "" -+ echo " Each library is combined into an xc-framework:" -+ echo " └── frameworks/" -+ echo " └── libfoo.xcframework" ++ echo " $SELF_NAME -o out/ios-release/filament/lib \\" ++ echo " out/ios-release/filament/lib/arm64-iphoneos \\" ++ echo " out/ios-release/filament/lib/universal" + echo "" -+ echo "Each should contain one or more single or universal-architecture static libraries." -+ echo "All s should contain the same number of libraries, with the same names." +} + +OUTPUT_DIR="" @@ -199,10 +188,11 @@ index 00000000000..cf720cb7e4e + +shift $((OPTIND - 1)) + -+PATHS=("$@") ++DEVICE_PATH="$1" ++SIMULATOR_PATH="$2" + -+if [[ ! "${PATHS[*]}" ]]; then -+ echo "One or more paths required." ++if [[ ! "${DEVICE_PATH}" ]] || [[ ! "${SIMULATOR_PATH}" ]]; then ++ echo "Both device and simulator paths required." + print_help + exit 1 +fi @@ -216,2943 +206,33 @@ index 00000000000..cf720cb7e4e +# Create the output directory, if it doesn't exist already. +mkdir -p "${OUTPUT_DIR}" + -+# Use the first path as the "leader" path. All paths should contain the same number of files with -+# the same names, so it doesn't matter which we chose. -+LEADER_PATH="${PATHS[0]}" -+ -+echo "Creating XC-Frameworks from path: ${LEADER_PATH}..." -+ -+# Loop through each file in the leader path. For each library we find, we'll collect additional -+# architectures in the other paths and combine them all into a universal library. -+for FILE in "${LEADER_PATH}"/*.a; do ++# Loop through each library in the device path and create an XCFramework ++for FILE in "${DEVICE_PATH}"/*.a; do + [ -f "${FILE}" ] || continue + + # The static library file name, like "libfilament.a" + LIBRARY_NAME="${FILE##*/}" + -+ INPUT_FILES=("-library ${LEADER_PATH}/${LIBRARY_NAME}") -+ for ARCH_PATH in "${PATHS[@]:1}"; do -+ THIS_FILE="${ARCH_PATH}/${LIBRARY_NAME}" -+ if [[ -f "${THIS_FILE}" ]]; then -+ INPUT_FILES+=("-library ${THIS_FILE}") -+ else -+ echo "Error: ${THIS_FILE} does not exist." -+ exit 1 -+ fi -+ done ++ # Framework name without extension, like "libfilament" ++ FRAMEWORK_NAME="${LIBRARY_NAME%.a}" + -+ # Remove the .a extension -+ LIBRARY_NAME="${LIBRARY_NAME%.a}" -+ -+ OUTPUT="${OUTPUT_DIR}/${LIBRARY_NAME}.xcframework" -+ # Delete previous xcframework -+ rm -rf $OUTPUT ++ echo "Creating XCFramework for library: ${LIBRARY_NAME}" + -+ # Create the xcframework command and execute it -+ CMD="xcodebuild -create-xcframework ${INPUT_FILES[@]} -output ${OUTPUT}" -+ eval $CMD -+done -diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec -index 3ed06a44db5..3ffe1632473 100644 ---- a/ios/CocoaPods/Filament.podspec -+++ b/ios/CocoaPods/Filament.podspec -@@ -8,12 +8,6 @@ Pod::Spec.new do |spec| - spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.3/filament-v1.51.3-ios.tgz" } - -- # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. -- spec.pod_target_xcconfig = { -- 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' -- } -- spec.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } -- - spec.subspec "filament" do |ss| - ss.source_files = - "include/filament/*.h", -@@ -23,13 +17,13 @@ Pod::Spec.new do |spec| - "include/ibl/*.h", - "include/geometry/*.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = -- "lib/universal/libfilament.a", -- "lib/universal/libbackend.a", -- "lib/universal/libfilabridge.a", -- "lib/universal/libfilaflat.a", -- "lib/universal/libibl.a", -- "lib/universal/libgeometry.a" -+ ss.vendored_frameworks = -+ "lib/libfilament.xcframework", -+ "lib/libbackend.xcframework", -+ "lib/libfilabridge.xcframework", -+ "lib/libfilaflat.xcframework", -+ "lib/libibl.xcframework", -+ "lib/libgeometry.xcframework" - ss.dependency "Filament/utils" - ss.dependency "Filament/math" - end -@@ -40,11 +34,11 @@ Pod::Spec.new do |spec| - "include/filament/MaterialChunkType.h", - "include/filament/MaterialEnums.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = -- "lib/universal/libfilamat.a", -- "lib/universal/libshaders.a", -- "lib/universal/libsmol-v.a", -- "lib/universal/libfilabridge.a" -+ ss.vendored_frameworks = -+ "lib/libfilamat.xcframework", -+ "lib/libshaders.xcframework", -+ "lib/libsmol-v.xcframework", -+ "lib/libfilabridge.xcframework" - ss.dependency "Filament/utils" - ss.dependency "Filament/math" - end -@@ -52,11 +46,11 @@ Pod::Spec.new do |spec| - spec.subspec "gltfio_core" do |ss| - ss.source_files = "include/gltfio/**/*.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = -- "lib/universal/libgltfio_core.a", -- "lib/universal/libdracodec.a", -- "lib/universal/libuberarchive.a", -- "lib/universal/libstb.a" -+ ss.vendored_frameworks = -+ "lib/libgltfio_core.xcframework", -+ "lib/libdracodec.xcframework", -+ "lib/libuberarchive.xcframework", -+ "lib/libstb.xcframework" - ss.dependency "Filament/filament" - ss.dependency "Filament/ktxreader" - ss.dependency "Filament/uberz" -@@ -64,23 +58,23 @@ Pod::Spec.new do |spec| - - spec.subspec "camutils" do |ss| - ss.source_files = "include/camutils/*.h" -- ss.vendored_libraries = "lib/universal/libcamutils.a" -+ ss.vendored_frameworks = "lib/libcamutils.xcframework" - ss.header_dir = "camutils" - ss.dependency "Filament/math" - end - - spec.subspec "filameshio" do |ss| - ss.source_files = "include/filameshio/*.h" -- ss.vendored_libraries = -- "lib/universal/libfilameshio.a", -- "lib/universal/libmeshoptimizer.a" -+ ss.vendored_frameworks = -+ "lib/libfilameshio.xcframework", -+ "lib/libmeshoptimizer.xcframework" - ss.header_dir = "filameshio" - ss.dependency "Filament/filament" - end - - spec.subspec "image" do |ss| - ss.source_files = "include/image/*.h" -- ss.vendored_libraries = "lib/universal/libimage.a" -+ ss.vendored_frameworks = "lib/libimage.xcframework" - ss.header_dir = "image" - ss.dependency "Filament/filament" - end -@@ -88,7 +82,7 @@ Pod::Spec.new do |spec| - spec.subspec "utils" do |ss| - ss.source_files = "include/utils/**/*.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = "lib/universal/libutils.a" -+ ss.vendored_frameworks = "lib/libutils.xcframework" - ss.dependency "Filament/tsl" - end - -@@ -105,9 +99,9 @@ Pod::Spec.new do |spec| - spec.subspec "ktxreader" do |ss| - ss.source_files = "include/ktxreader/*.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = -- "lib/universal/libktxreader.a", -- "lib/universal/libbasis_transcoder.a" -+ ss.vendored_frameworks = -+ "lib/libktxreader.xcframework", -+ "lib/libbasis_transcoder.xcframework" - ss.dependency "Filament/image" - ss.dependency "Filament/filament" - end -@@ -115,9 +109,9 @@ Pod::Spec.new do |spec| - spec.subspec "viewer" do |ss| - ss.source_files = "include/viewer/*.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = -- "lib/universal/libviewer.a", -- "lib/universal/libcivetweb.a" -+ ss.vendored_frameworks = -+ "lib/libviewer.xcframework", -+ "lib/libcivetweb.xcframework" - ss.dependency "Filament/filament" - ss.dependency "Filament/gltfio_core" - end -@@ -125,9 +119,9 @@ Pod::Spec.new do |spec| - spec.subspec "uberz" do |ss| - ss.source_files = "include/uberz/*.h" - ss.header_mappings_dir = "include" -- ss.vendored_libraries = -- "lib/universal/libuberzlib.a", -- "lib/universal/libzstd.a" -+ ss.vendored_frameworks = -+ "lib/libuberzlib.xcframework", -+ "lib/libzstd.xcframework" - ss.header_dir = "uberz" - ss.dependency "Filament/filamat" - ss.dependency "Filament/tsl" -diff --git a/ios/samples/README.md b/ios/samples/README.md -index 4c132849ee4..6c9c165eabb 100644 ---- a/ios/samples/README.md -+++ b/ios/samples/README.md -@@ -47,7 +47,7 @@ build Filament in Release mode, replace `debug` with `release` in the above `bui - - If you also want to be able to run on the iOS simulator, add the `-s` flag to the `build.sh` - command. For example, the following command will build for both devices (ARM64) and the simulator --(x86_64) in Debug mode: -+(x86_64/ARM64) in Debug mode: - - ``` - $ ./build.sh -s -p ios -i debug -diff --git a/ios/samples/app-template.yml b/ios/samples/app-template.yml -index e8e14c50715..9c407448d16 100644 ---- a/ios/samples/app-template.yml -+++ b/ios/samples/app-template.yml -@@ -17,25 +17,27 @@ targetTemplates: - configVariants: - - Metal - - OpenGL -+ dependencies: -+ - framework: "../../out/ios-release/filament/lib/libbackend.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libfilament.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libfilaflat.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libktxreader.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libfilabridge.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libutils.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libsmol-v.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libgeometry.xcframework" -+ - framework: "../../out/ios-release/filament/lib/libibl.xcframework" - settings: - base: -- OTHER_LDFLAGS: ["-lfilament", "-lbackend", "-lfilaflat", "-lktxreader", -- "-lfilabridge", "-lutils", "-lsmol-v", "-lgeometry", "-libl"] - ENABLE_BITCODE: NO - CLANG_CXX_LANGUAGE_STANDARD: gnu++17 - # This allows users to not have to specify a unique bundle ID when building the sample apps. - SAMPLE_CODE_DISAMBIGUATOR: ${DEVELOPMENT_TEAM} - configs: - debug: -- HEADER_SEARCH_PATHS: ["../../../out/ios-debug/filament/include", "generated"] -- LIBRARY_SEARCH_PATHS: -- - "../../../out/ios-debug/filament/lib/$(CURRENT_ARCH)" -- - "../../../out/ios-debug/filament/lib/universal" -+ HEADER_SEARCH_PATHS: ["../../../out/ios-debug/filament/include", "generated"] - release: -- HEADER_SEARCH_PATHS: ["../../../out/ios-release/filament/include", "generated"] -- LIBRARY_SEARCH_PATHS: -- - "../../../out/ios-release/filament/lib/$(CURRENT_ARCH)" -- - "../../../out/ios-release/filament/lib/universal" -+ HEADER_SEARCH_PATHS: ["../../../out/ios-release/filament/include", "generated"] - metal: - GCC_PREPROCESSOR_DEFINITIONS: ["FILAMENT_APP_USE_METAL=1", "$(inherited)"] - opengl: -diff --git a/ios/samples/backend-test/backend-test.xcodeproj/project.pbxproj b/ios/samples/backend-test/backend-test.xcodeproj/project.pbxproj -index b47f4c00e36..ea4c11fe046 100644 ---- a/ios/samples/backend-test/backend-test.xcodeproj/project.pbxproj -+++ b/ios/samples/backend-test/backend-test.xcodeproj/project.pbxproj -@@ -3,34 +3,112 @@ - archiveVersion = 1; - classes = { - }; -- objectVersion = 51; -+ objectVersion = 54; - objects = { - - /* Begin PBXBuildFile section */ -+ 0DE5DD77BF1B9049D63B76D0 /* libutils.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7C969D515221A7739C68F3AC /* libutils.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 20863805FC626BF952BA3275 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 33D80A4472074C601C57C773 /* ViewController.mm */; }; -+ 2AABB2C1262EBA9553666078 /* libsmol-v.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8F108854ADC54BBAA29C2055 /* libsmol-v.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ 32AC9F2183DD11F130D4A91D /* libfilament.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 912652335565C18B252345DE /* libfilament.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 376F63E72973F07BF618082D /* FilamentView.mm in Sources */ = {isa = PBXBuildFile; fileRef = B54DFEB4DB6ACC0289FA3046 /* FilamentView.mm */; }; -+ 3D73E90782CC3A2124DF046F /* libfilaflat.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E8375B927AFDFD1069016D1 /* libfilaflat.xcframework */; }; - 477A2A67D32B7A3AF2E7B2F4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EB0A767D257F3D05BE9F0960 /* LaunchScreen.storyboard */; }; -+ 66014C413BCBF00E8E6DAF76 /* libgeometry.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB3F32D8D3D7310FA3A0C1A1 /* libgeometry.xcframework */; }; -+ 6A5D5E984CA862B943742745 /* libgeometry.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DB3F32D8D3D7310FA3A0C1A1 /* libgeometry.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ 7D8D3494388D7BF4D9FE4FCE /* libfilamat.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4BDDF85AADA2069C0F9FBAE /* libfilamat.xcframework */; }; -+ 7E671E777FDB5F34AAA13D5A /* libutils.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C969D515221A7739C68F3AC /* libutils.xcframework */; }; -+ 8095A6D37F6F1C9C13BBFEA8 /* libfilamat.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C4BDDF85AADA2069C0F9FBAE /* libfilamat.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ 8402B2AC6D081D1E8E065552 /* libbackend.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8632D4625CFCBD32A46772B5 /* libbackend.xcframework */; }; -+ 88C039F10343B8598FD5FF7F /* libfilabridge.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2A3753419FC4A7D4C039868 /* libfilabridge.xcframework */; }; - 91CCBA3BA96DC289D0E8E975 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AE6C78F32A0494087B0CEF32 /* main.mm */; }; -+ AA5AB498C5A6117057B06824 /* libfilaflat.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9E8375B927AFDFD1069016D1 /* libfilaflat.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ AE63AFEA7E7D507BB677C158 /* libbackend.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8632D4625CFCBD32A46772B5 /* libbackend.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - B3684DB18E82C867020294F1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5FDB89AD408A508D6CB74C32 /* AppDelegate.m */; }; -+ B73B15843FCEEEB637C636F4 /* libbackend_test.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 01B66093324E8D661CBEBB0F /* libbackend_test.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ BFE92829375EE1DBA1E4A689 /* libfilabridge.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B2A3753419FC4A7D4C039868 /* libfilabridge.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - C74DDEF71B6F3C060BB56189 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B63A6E45E40F58037ABD8F84 /* Main.storyboard */; }; -+ C8C8BF3416B9B37003F1031E /* libktxreader.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 19CBBB6C426E8D038BDAC97B /* libktxreader.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ D0E54C592A378E549C96A54D /* libibl.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D1E30700B368B5F84196FD97 /* libibl.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -+ D472EB54D462394FE99DCE2B /* libibl.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = D1E30700B368B5F84196FD97 /* libibl.xcframework */; }; -+ D5B2F0A85F7C6F352AA99DF1 /* libsmol-v.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F108854ADC54BBAA29C2055 /* libsmol-v.xcframework */; }; -+ F571BE7CBFBADBDF42C7C23F /* libbackend_test.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01B66093324E8D661CBEBB0F /* libbackend_test.xcframework */; }; -+ F5E844BFD77174F770543A87 /* libfilament.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 912652335565C18B252345DE /* libfilament.xcframework */; }; -+ FB393C813D7A30404A2DEF23 /* libktxreader.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 19CBBB6C426E8D038BDAC97B /* libktxreader.xcframework */; }; - FC9D623AC5CF31BA9FB0C47B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 93394D7D4A78C56A2BD31A7D /* Assets.xcassets */; }; - /* End PBXBuildFile section */ - -+/* Begin PBXCopyFilesBuildPhase section */ -+ 0ABB76F2D4011A4AF5186FCA /* Embed Frameworks */ = { -+ isa = PBXCopyFilesBuildPhase; -+ buildActionMask = 2147483647; -+ dstPath = ""; -+ dstSubfolderSpec = 10; -+ files = ( -+ AE63AFEA7E7D507BB677C158 /* libbackend.xcframework in Embed Frameworks */, -+ 32AC9F2183DD11F130D4A91D /* libfilament.xcframework in Embed Frameworks */, -+ AA5AB498C5A6117057B06824 /* libfilaflat.xcframework in Embed Frameworks */, -+ C8C8BF3416B9B37003F1031E /* libktxreader.xcframework in Embed Frameworks */, -+ BFE92829375EE1DBA1E4A689 /* libfilabridge.xcframework in Embed Frameworks */, -+ 0DE5DD77BF1B9049D63B76D0 /* libutils.xcframework in Embed Frameworks */, -+ 2AABB2C1262EBA9553666078 /* libsmol-v.xcframework in Embed Frameworks */, -+ 6A5D5E984CA862B943742745 /* libgeometry.xcframework in Embed Frameworks */, -+ D0E54C592A378E549C96A54D /* libibl.xcframework in Embed Frameworks */, -+ B73B15843FCEEEB637C636F4 /* libbackend_test.xcframework in Embed Frameworks */, -+ 8095A6D37F6F1C9C13BBFEA8 /* libfilamat.xcframework in Embed Frameworks */, -+ ); -+ name = "Embed Frameworks"; -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXCopyFilesBuildPhase section */ ++ SIMULATOR_FILE="${SIMULATOR_PATH}/${LIBRARY_NAME}" ++ if [[ ! -f "${SIMULATOR_FILE}" ]]; then ++ echo "Warning: ${SIMULATOR_FILE} does not exist. Skipping." ++ continue ++ fi + - /* Begin PBXFileReference section */ -+ 01B66093324E8D661CBEBB0F /* libbackend_test.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libbackend_test.xcframework; path = "$(SRCROOT)/../../../out/ios-release/filament/lib/libbackend_test.xcframework"; sourceTree = ""; }; - 056C16E7805E8D33D4C8F7B4 /* FilamentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilamentView.h; sourceTree = ""; }; - 13DDE7723EC1A6EFCA4D2D58 /* backend-test.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = "backend-test.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 17364ECD407006EEBDC06024 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; -+ 19CBBB6C426E8D038BDAC97B /* libktxreader.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libktxreader.xcframework; path = "../../../out/ios-release/filament/lib/libktxreader.xcframework"; sourceTree = ""; }; - 276A0ADD5B521719BBB1DA21 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 33D80A4472074C601C57C773 /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = ""; }; - 5FDB89AD408A508D6CB74C32 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 603475CEE63507AA04A9B21A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; -+ 7C969D515221A7739C68F3AC /* libutils.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libutils.xcframework; path = "../../../out/ios-release/filament/lib/libutils.xcframework"; sourceTree = ""; }; -+ 8632D4625CFCBD32A46772B5 /* libbackend.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libbackend.xcframework; path = "../../../out/ios-release/filament/lib/libbackend.xcframework"; sourceTree = ""; }; -+ 8F108854ADC54BBAA29C2055 /* libsmol-v.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "libsmol-v.xcframework"; path = "../../../out/ios-release/filament/lib/libsmol-v.xcframework"; sourceTree = ""; }; -+ 912652335565C18B252345DE /* libfilament.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libfilament.xcframework; path = "../../../out/ios-release/filament/lib/libfilament.xcframework"; sourceTree = ""; }; - 93394D7D4A78C56A2BD31A7D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; -+ 9E8375B927AFDFD1069016D1 /* libfilaflat.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libfilaflat.xcframework; path = "../../../out/ios-release/filament/lib/libfilaflat.xcframework"; sourceTree = ""; }; - A283B2D6DB365368855E2D7A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - AE6C78F32A0494087B0CEF32 /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; -+ B2A3753419FC4A7D4C039868 /* libfilabridge.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libfilabridge.xcframework; path = "../../../out/ios-release/filament/lib/libfilabridge.xcframework"; sourceTree = ""; }; - B54DFEB4DB6ACC0289FA3046 /* FilamentView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FilamentView.mm; sourceTree = ""; }; -+ C4BDDF85AADA2069C0F9FBAE /* libfilamat.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libfilamat.xcframework; path = "$(SRCROOT)/../../../out/ios-release/filament/lib/libfilamat.xcframework"; sourceTree = ""; }; - C76230D48038E8109EC121B0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; -+ D1E30700B368B5F84196FD97 /* libibl.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libibl.xcframework; path = "../../../out/ios-release/filament/lib/libibl.xcframework"; sourceTree = ""; }; -+ DB3F32D8D3D7310FA3A0C1A1 /* libgeometry.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = libgeometry.xcframework; path = "../../../out/ios-release/filament/lib/libgeometry.xcframework"; sourceTree = ""; }; - /* End PBXFileReference section */ - -+/* Begin PBXFrameworksBuildPhase section */ -+ 5E31B50ED1338E6ADE1FA49A /* Frameworks */ = { -+ isa = PBXFrameworksBuildPhase; -+ buildActionMask = 2147483647; -+ files = ( -+ 8402B2AC6D081D1E8E065552 /* libbackend.xcframework in Frameworks */, -+ F5E844BFD77174F770543A87 /* libfilament.xcframework in Frameworks */, -+ 3D73E90782CC3A2124DF046F /* libfilaflat.xcframework in Frameworks */, -+ FB393C813D7A30404A2DEF23 /* libktxreader.xcframework in Frameworks */, -+ 88C039F10343B8598FD5FF7F /* libfilabridge.xcframework in Frameworks */, -+ 7E671E777FDB5F34AAA13D5A /* libutils.xcframework in Frameworks */, -+ D5B2F0A85F7C6F352AA99DF1 /* libsmol-v.xcframework in Frameworks */, -+ 66014C413BCBF00E8E6DAF76 /* libgeometry.xcframework in Frameworks */, -+ D472EB54D462394FE99DCE2B /* libibl.xcframework in Frameworks */, -+ F571BE7CBFBADBDF42C7C23F /* libbackend_test.xcframework in Frameworks */, -+ 7D8D3494388D7BF4D9FE4FCE /* libfilamat.xcframework in Frameworks */, -+ ); -+ runOnlyForDeploymentPostprocessing = 0; -+ }; -+/* End PBXFrameworksBuildPhase section */ -+ - /* Begin PBXGroup section */ - 05CA69F10D830E40A8FB561D /* Products */ = { - isa = PBXGroup; -@@ -44,10 +122,29 @@ - isa = PBXGroup; - children = ( - FE198F0E1EF047534F8F3128 /* backend-test */, -+ 81BF22C13654E58F622E6BEC /* Frameworks */, - 05CA69F10D830E40A8FB561D /* Products */, - ); - sourceTree = ""; - }; -+ 81BF22C13654E58F622E6BEC /* Frameworks */ = { -+ isa = PBXGroup; -+ children = ( -+ 01B66093324E8D661CBEBB0F /* libbackend_test.xcframework */, -+ 8632D4625CFCBD32A46772B5 /* libbackend.xcframework */, -+ B2A3753419FC4A7D4C039868 /* libfilabridge.xcframework */, -+ 9E8375B927AFDFD1069016D1 /* libfilaflat.xcframework */, -+ C4BDDF85AADA2069C0F9FBAE /* libfilamat.xcframework */, -+ 912652335565C18B252345DE /* libfilament.xcframework */, -+ DB3F32D8D3D7310FA3A0C1A1 /* libgeometry.xcframework */, -+ D1E30700B368B5F84196FD97 /* libibl.xcframework */, -+ 19CBBB6C426E8D038BDAC97B /* libktxreader.xcframework */, -+ 8F108854ADC54BBAA29C2055 /* libsmol-v.xcframework */, -+ 7C969D515221A7739C68F3AC /* libutils.xcframework */, -+ ); -+ name = Frameworks; -+ sourceTree = ""; -+ }; - FE198F0E1EF047534F8F3128 /* backend-test */ = { - isa = PBXGroup; - children = ( -@@ -75,6 +172,8 @@ - buildPhases = ( - 445863ED54C294354A388281 /* Sources */, - BD71575ED7ED3237FEFD94C7 /* Resources */, -+ 5E31B50ED1338E6ADE1FA49A /* Frameworks */, -+ 0ABB76F2D4011A4AF5186FCA /* Embed Frameworks */, - ); - buildRules = ( - ); -@@ -91,12 +190,13 @@ - 8A7A83D59A7827D3335343C2 /* Project object */ = { - isa = PBXProject; - attributes = { -- LastUpgradeCheck = 1200; -+ BuildIndependentTargetsInParallel = YES; -+ LastUpgradeCheck = 1430; - TargetAttributes = { - }; - }; - buildConfigurationList = E89E1EEA671E49DBAC9D5A9C /* Build configuration list for PBXProject "backend-test" */; -- compatibilityVersion = "Xcode 11.0"; -+ compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( -@@ -221,6 +321,11 @@ - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_IDENTITY = "iPhone Developer"; - ENABLE_BITCODE = NO; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "\"$(SRCROOT)/../../../out/ios-release/filament/lib\"", -+ "\"../../../out/ios-release/filament/lib\"", -+ ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "FILAMENT_APP_USE_OPENGL=1", - "$(inherited)", -@@ -235,22 +340,8 @@ - "$(inherited)", - "@executable_path/Frameworks", - ); -- LIBRARY_SEARCH_PATHS = ( -- "../../../out/ios-debug/filament/lib/$(CURRENT_ARCH)", -- "../../../out/ios-debug/filament/lib/universal", -- ); - OTHER_LDFLAGS = ( -- "-lfilament", -- "-lbackend", -- "-lfilaflat", -- "-lktxreader", -- "-lfilabridge", -- "-lutils", -- "-lsmol-v", -- "-lgeometry", -- "-libl", -- "-lfilamat", -- "-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a", -+ "-force_load ../../../out/ios-release/filament/lib/arm64/libbackend_test.xcframework", - ); - PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test"; - SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}"; -@@ -328,6 +419,11 @@ - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_IDENTITY = "iPhone Developer"; - ENABLE_BITCODE = NO; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "\"$(SRCROOT)/../../../out/ios-release/filament/lib\"", -+ "\"../../../out/ios-release/filament/lib\"", -+ ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "FILAMENT_APP_USE_OPENGL=1", - "$(inherited)", -@@ -342,22 +438,8 @@ - "$(inherited)", - "@executable_path/Frameworks", - ); -- LIBRARY_SEARCH_PATHS = ( -- "../../../out/ios-release/filament/lib/$(CURRENT_ARCH)", -- "../../../out/ios-release/filament/lib/universal", -- ); - OTHER_LDFLAGS = ( -- "-lfilament", -- "-lbackend", -- "-lfilaflat", -- "-lktxreader", -- "-lfilabridge", -- "-lutils", -- "-lsmol-v", -- "-lgeometry", -- "-libl", -- "-lfilamat", -- "-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a", -+ "-force_load ../../../out/ios-release/filament/lib/arm64/libbackend_test.xcframework", - ); - PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test"; - SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}"; -@@ -428,6 +510,11 @@ - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_IDENTITY = "iPhone Developer"; - ENABLE_BITCODE = NO; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "\"$(SRCROOT)/../../../out/ios-release/filament/lib\"", -+ "\"../../../out/ios-release/filament/lib\"", -+ ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "FILAMENT_APP_USE_METAL=1", - "$(inherited)", -@@ -442,22 +529,8 @@ - "$(inherited)", - "@executable_path/Frameworks", - ); -- LIBRARY_SEARCH_PATHS = ( -- "../../../out/ios-debug/filament/lib/$(CURRENT_ARCH)", -- "../../../out/ios-debug/filament/lib/universal", -- ); - OTHER_LDFLAGS = ( -- "-lfilament", -- "-lbackend", -- "-lfilaflat", -- "-lktxreader", -- "-lfilabridge", -- "-lutils", -- "-lsmol-v", -- "-lgeometry", -- "-libl", -- "-lfilamat", -- "-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a", -+ "-force_load ../../../out/ios-release/filament/lib/arm64/libbackend_test.xcframework", - ); - PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test"; - SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}"; -@@ -473,6 +546,11 @@ - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CODE_SIGN_IDENTITY = "iPhone Developer"; - ENABLE_BITCODE = NO; -+ FRAMEWORK_SEARCH_PATHS = ( -+ "$(inherited)", -+ "\"$(SRCROOT)/../../../out/ios-release/filament/lib\"", -+ "\"../../../out/ios-release/filament/lib\"", -+ ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "FILAMENT_APP_USE_METAL=1", - "$(inherited)", -@@ -487,22 +565,8 @@ - "$(inherited)", - "@executable_path/Frameworks", - ); -- LIBRARY_SEARCH_PATHS = ( -- "../../../out/ios-release/filament/lib/$(CURRENT_ARCH)", -- "../../../out/ios-release/filament/lib/universal", -- ); - OTHER_LDFLAGS = ( -- "-lfilament", -- "-lbackend", -- "-lfilaflat", -- "-lktxreader", -- "-lfilabridge", -- "-lutils", -- "-lsmol-v", -- "-lgeometry", -- "-libl", -- "-lfilamat", -- "-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a", -+ "-force_load ../../../out/ios-release/filament/lib/arm64/libbackend_test.xcframework", - ); - PRODUCT_BUNDLE_IDENTIFIER = "${SAMPLE_CODE_DISAMBIGUATOR}.google.filament.backend-test"; - SAMPLE_CODE_DISAMBIGUATOR = "${DEVELOPMENT_TEAM}"; -diff --git a/ios/samples/backend-test/backend-test.xcodeproj/xcshareddata/xcschemes/backend-test Metal.xcscheme b/ios/samples/backend-test/backend-test.xcodeproj/xcshareddata/xcschemes/backend-test Metal.xcscheme -index f4026437bcd..d6462758330 100644 ---- a/ios/samples/backend-test/backend-test.xcodeproj/xcshareddata/xcschemes/backend-test Metal.xcscheme -+++ b/ios/samples/backend-test/backend-test.xcodeproj/xcshareddata/xcschemes/backend-test Metal.xcscheme -@@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ T getParameter(const char* UTILS_NONNULL name, size_t nameLength) const; -+ -+ /** inline helper to provide the name as a null-terminated C string */ -+ template> -+ inline T getParameter(StringLiteral name) const { -+ return getParameter(name.data, name.size); -+ } -+ -+ /** inline helper to provide the name as a null-terminated C string */ -+ template> -+ inline T getParameter(const char* UTILS_NONNULL name) const { -+ return getParameter(name, strlen(name)); -+ } -+ - /** - * Set-up a custom scissor rectangle; by default it is disabled. - * -diff --git a/filament/src/MaterialInstance.cpp b/filament/src/MaterialInstance.cpp -index 33f3ab764..ee0af2de1 100644 ---- a/filament/src/MaterialInstance.cpp -+++ b/filament/src/MaterialInstance.cpp -@@ -75,6 +75,12 @@ inline void FMaterialInstance::setParameterImpl(std::string_view name, - setParameterUntypedImpl(name, value, count); - } - -+template -+T FMaterialInstance::getParameterImpl(std::string_view name) const noexcept { -+ ssize_t offset = mMaterial->getUniformInterfaceBlock().getFieldOffset(name, 0); -+ return downcast(this)->getUniformBuffer().getUniform(offset); -+} -+ - // ------------------------------------------------------------------------------------------------ - - template -@@ -183,6 +189,28 @@ template UTILS_PUBLIC void MaterialInstance::setParameter (const char* - - // ------------------------------------------------------------------------------------------------ - -+template -+T MaterialInstance::getParameter(const char* name, size_t nameLength) const { -+ return downcast(this)->getParameterImpl({ name, nameLength }); -+} -+ -+// explicit template instantiation of our supported types -+template UTILS_PUBLIC float MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC int32_t MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC uint32_t MaterialInstance::getParameter(const char* name, size_t nameLength) const; -+template UTILS_PUBLIC int2 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC int3 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC int4 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC uint2 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC uint3 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC uint4 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC float2 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC float3 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC float4 MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+template UTILS_PUBLIC mat3f MaterialInstance::getParameter (const char* name, size_t nameLength) const; -+ -+// ------------------------------------------------------------------------------------------------ -+ - Material const* MaterialInstance::getMaterial() const noexcept { - return downcast(this)->getMaterial(); - } -diff --git a/filament/src/details/MaterialInstance.h b/filament/src/details/MaterialInstance.h -index 6be23b7e0..2f05bcf3f 100644 ---- a/filament/src/details/MaterialInstance.h -+++ b/filament/src/details/MaterialInstance.h -@@ -231,6 +231,9 @@ private: - void setParameterImpl(std::string_view name, - FTexture const* texture, TextureSampler const& sampler); - -+ template -+ T getParameterImpl(std::string_view name) const noexcept; -+ - FMaterialInstance() noexcept; - void initDefaultInstance(FEngine& engine, FMaterial const* material); - diff --git a/package/ios/libs/filament/README.md b/package/ios/libs/filament/README.md index 79ec596b..abe815eb 100644 --- a/package/ios/libs/filament/README.md +++ b/package/ios/libs/filament/README.md @@ -92,14 +92,14 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory. ### Linux ```make -FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl +FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil CC=clang++ main: main.o $(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) -lpthread -lc++ -ldl -o main main.o: main.cpp - $(CC) -Iinclude/ -std=c++17 -pthread -c main.cpp + $(CC) -Iinclude/ -std=c++20 -pthread -c main.cpp clean: rm -f main main.o @@ -110,15 +110,16 @@ clean: ### macOS ```make -FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl +FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo CC=clang++ +ARCH ?= $(shell uname -m) main: main.o - $(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main + $(CC) -Llib/$(ARCH)/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main main.o: main.cpp - $(CC) -Iinclude/ -std=c++17 -c main.cpp + $(CC) -Iinclude/ -std=c++20 -c main.cpp clean: rm -f main main.o @@ -139,7 +140,7 @@ used to change the run-time library version. ```make FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \ - utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib + utils.lib geometry.lib smol-v.lib ibl.lib abseil.lib CC=cl.exe main.exe: main.obj @@ -147,7 +148,7 @@ main.exe: main.obj gdi32.lib user32.lib opengl32.lib main.obj: main.cpp - $(CC) /MT /Iinclude\\ /std:c++17 /c main.cpp + $(CC) /MT /Iinclude\\ /std:c++20 /c main.cpp clean: del main.exe main.obj diff --git a/package/ios/libs/filament/include/backend/AcquiredImage.h b/package/ios/libs/filament/include/backend/AcquiredImage.h index fec27a53..ff0840e5 100644 --- a/package/ios/libs/filament/include/backend/AcquiredImage.h +++ b/package/ios/libs/filament/include/backend/AcquiredImage.h @@ -18,6 +18,7 @@ #define TNT_FILAMENT_BACKEND_PRIVATE_ACQUIREDIMAGE_H #include +#include namespace filament::backend { diff --git a/package/ios/libs/filament/include/backend/BufferDescriptor.h b/package/ios/libs/filament/include/backend/BufferDescriptor.h index ebb57537..afc0da20 100644 --- a/package/ios/libs/filament/include/backend/BufferDescriptor.h +++ b/package/ios/libs/filament/include/backend/BufferDescriptor.h @@ -20,10 +20,15 @@ #define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H #include -#include + +#include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { class CallbackHandler; @@ -89,8 +94,8 @@ class UTILS_PUBLIC BufferDescriptor { * @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, - Callback callback = nullptr, void* user = nullptr) noexcept + BufferDescriptor(void const* buffer, size_t const size, + Callback const callback = nullptr, void* user = nullptr) noexcept : buffer(const_cast(buffer)), size(size), mCallback(callback), mUser(user) { } @@ -98,11 +103,12 @@ class UTILS_PUBLIC BufferDescriptor { * 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 handler A custom handler for the callback * @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 + BufferDescriptor(void const* buffer, size_t const size, + CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept : buffer(const_cast(buffer)), size(size), mCallback(callback), mUser(user), mHandler(handler) { } @@ -116,8 +122,9 @@ class UTILS_PUBLIC BufferDescriptor { * * @param buffer Memory address of the CPU buffer to reference * @param size Size of the CPU buffer in bytes + * @param data A pointer to the data * @param handler Handler to use to dispatch the callback, or nullptr for the default handler - * @return a new BufferDescriptor + * @return A new BufferDescriptor */ template static BufferDescriptor make(void const* buffer, size_t size, T* data, @@ -164,7 +171,7 @@ class UTILS_PUBLIC BufferDescriptor { * @param callback The new callback function * @param user An opaque user pointer passed to the callbeck function when it's called */ - void setCallback(Callback callback, void* user = nullptr) noexcept { + void setCallback(Callback const callback, void* user = nullptr) noexcept { this->mCallback = callback; this->mUser = user; this->mHandler = nullptr; @@ -176,7 +183,7 @@ class UTILS_PUBLIC BufferDescriptor { * @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 { + void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept { mCallback = callback; mUser = user; mHandler = handler; diff --git a/package/ios/libs/filament/include/backend/CallbackHandler.h b/package/ios/libs/filament/include/backend/CallbackHandler.h index 036031a9..9700186d 100644 --- a/package/ios/libs/filament/include/backend/CallbackHandler.h +++ b/package/ios/libs/filament/include/backend/CallbackHandler.h @@ -64,7 +64,7 @@ class CallbackHandler { virtual void post(void* user, Callback callback) = 0; protected: - virtual ~CallbackHandler() = default; + virtual ~CallbackHandler(); }; } // namespace filament::backend diff --git a/package/ios/libs/filament/include/backend/DescriptorSetOffsetArray.h b/package/ios/libs/filament/include/backend/DescriptorSetOffsetArray.h new file mode 100644 index 00000000..d7cfb763 --- /dev/null +++ b/package/ios/libs/filament/include/backend/DescriptorSetOffsetArray.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2024 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_COMMANDSTREAMVECTOR_H +#define TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H + +#include + +#include +#include + +#include +#include + + +namespace utils::io { +class ostream; +} // namespace utils::io + +namespace filament::backend { + +void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept; + +class DescriptorSetOffsetArray { +public: + using value_type = uint32_t; + using reference = value_type&; + using const_reference = value_type const&; + using size_type = uint32_t; + using difference_type = int32_t; + using pointer = value_type*; + using const_pointer = value_type const*; + using iterator = pointer; + using const_iterator = const_pointer; + + DescriptorSetOffsetArray() noexcept = default; + + ~DescriptorSetOffsetArray() noexcept = default; + + DescriptorSetOffsetArray(size_type size, DriverApi& driver) noexcept { + mOffsets = (value_type *)allocateFromCommandStream(driver, + size * sizeof(value_type), alignof(value_type)); + std::uninitialized_fill_n(mOffsets, size, 0); + } + + DescriptorSetOffsetArray(std::initializer_list list, DriverApi& driver) noexcept { + mOffsets = (value_type *)allocateFromCommandStream(driver, + list.size() * sizeof(value_type), alignof(value_type)); + std::uninitialized_copy(list.begin(), list.end(), mOffsets); + } + + DescriptorSetOffsetArray(DescriptorSetOffsetArray const&) = delete; + DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray const&) = delete; + + DescriptorSetOffsetArray(DescriptorSetOffsetArray&& rhs) noexcept + : mOffsets(rhs.mOffsets) { + rhs.mOffsets = nullptr; + } + + DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray&& rhs) noexcept { + if (this != &rhs) { + mOffsets = rhs.mOffsets; + rhs.mOffsets = nullptr; + } + return *this; + } + + bool empty() const noexcept { return mOffsets == nullptr; } + + value_type* data() noexcept { return mOffsets; } + const value_type* data() const noexcept { return mOffsets; } + + + reference operator[](size_type n) noexcept { + return *(data() + n); + } + + const_reference operator[](size_type n) const noexcept { + return *(data() + n); + } + + void clear() noexcept { + mOffsets = nullptr; + } + +private: + value_type *mOffsets = nullptr; +}; + +} // namespace filament::backend + +#if !defined(NDEBUG) +utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::DescriptorSetOffsetArray& rhs); +#endif + +#endif //TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H diff --git a/package/ios/libs/filament/include/backend/DriverEnums.h b/package/ios/libs/filament/include/backend/DriverEnums.h index ef1c655c..3b1ae238 100644 --- a/package/ios/libs/filament/include/backend/DriverEnums.h +++ b/package/ios/libs/filament/include/backend/DriverEnums.h @@ -19,21 +19,32 @@ #ifndef TNT_FILAMENT_BACKEND_DRIVERENUMS_H #define TNT_FILAMENT_BACKEND_DRIVERENUMS_H -#include #include // Because we define ERROR in the FenceStatus enum. +#include #include -#include +#include +#include +#include +#include +#include +#include #include -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers +#include +#include +#include +#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + /** * Types and enums used by filament's driver. * @@ -90,11 +101,26 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON */ static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40; +/** + * Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the + * given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is + * true. + * + * This is only supported by EGL(Android). Other GL platforms (GLX, WGL, etc) don't support it + * because the swapchain MSAA settings must be configured before window creation. + */ +static constexpr uint64_t SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES = 0x80; static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES. static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3. static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects. static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES. +static constexpr size_t MAX_DESCRIPTOR_SET_COUNT = 4; // This is guaranteed by Vulkan. +static constexpr size_t MAX_DESCRIPTOR_COUNT = 64; // per set + +static constexpr size_t MAX_PUSH_CONSTANT_COUNT = 32; // Vulkan 1.1 spec allows for 128-byte + // of push constant (we assume 4-byte + // types). // Per feature level caps // Use (int)FeatureLevel to index this array @@ -103,8 +129,8 @@ static constexpr struct { const size_t MAX_FRAGMENT_SAMPLER_COUNT; } FEATURE_LEVEL_CAPS[4] = { { 0, 0 }, // do not use - { 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal - { 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal + { 16, 16 }, // guaranteed by OpenGL ES, Vulkan, Metal And WebGPU + { 16, 16 }, // guaranteed by OpenGL ES, Vulkan, Metal And WebGPU { 31, 31 }, // guaranteed by Metal }; @@ -112,9 +138,13 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT, "The number of buffer objects that can be attached to a VertexBuffer must be " "less than or equal to the maximum number of vertex attributes."); -static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES. +static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES. static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES. +static constexpr uint8_t EXTERNAL_SAMPLER_DATA_INDEX_UNUSED = + uint8_t(-1);// Case where the descriptor set binding isnt using any external sampler state + // and therefore doesn't have a valid entry. + /** * Defines the backend's feature levels. */ @@ -133,7 +163,8 @@ enum class Backend : uint8_t { OPENGL = 1, //!< Selects the OpenGL/ES driver (default on Android) VULKAN = 2, //!< Selects the Vulkan driver if the platform supports it (default on Linux/Windows) METAL = 3, //!< Selects the Metal driver if the platform supports it (default on MacOS/iOS). - NOOP = 4, //!< Selects the no-op driver for testing purposes. + WEBGPU = 4, //!< Selects the Webgpu driver if the platform supports webgpu. + NOOP = 5, //!< Selects the no-op driver for testing purposes. }; enum class TimerQueryResult : int8_t { @@ -142,7 +173,7 @@ enum class TimerQueryResult : int8_t { AVAILABLE = 1, // result is available }; -static constexpr const char* backendToString(Backend backend) { +constexpr std::string_view to_string(Backend const backend) noexcept { switch (backend) { case Backend::NOOP: return "Noop"; @@ -152,9 +183,12 @@ static constexpr const char* backendToString(Backend backend) { return "Vulkan"; case Backend::METAL: return "Metal"; - default: - return "Unknown"; + case Backend::WEBGPU: + return "WebGPU"; + case Backend::DEFAULT: + return "Default"; } + return "Unknown"; } /** @@ -163,14 +197,16 @@ static constexpr const char* backendToString(Backend backend) { * - The Metal backend can prefer precompiled Metal libraries, while falling back to MSL. */ enum class ShaderLanguage { + UNSPECIFIED = -1, ESSL1 = 0, ESSL3 = 1, SPIRV = 2, MSL = 3, METAL_LIBRARY = 4, + WGSL = 5, }; -static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) { +constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noexcept { switch (shaderLanguage) { case ShaderLanguage::ESSL1: return "ESSL 1.0"; @@ -182,9 +218,310 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag return "MSL"; case ShaderLanguage::METAL_LIBRARY: return "Metal precompiled library"; + case ShaderLanguage::WGSL: + return "WGSL"; + case ShaderLanguage::UNSPECIFIED: + return "Unspecified"; } + return "UNKNOWN"; } +enum class ShaderStage : uint8_t { + VERTEX = 0, + FRAGMENT = 1, + COMPUTE = 2 +}; + +static constexpr size_t PIPELINE_STAGE_COUNT = 2; +enum class ShaderStageFlags : uint8_t { + NONE = 0, + VERTEX = 0x1, + FRAGMENT = 0x2, + COMPUTE = 0x4, + ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE +}; + +constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { + switch (type) { + case ShaderStage::VERTEX: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); + case ShaderStage::FRAGMENT: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT)); + case ShaderStage::COMPUTE: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE)); + } +} + +enum class TextureType : uint8_t { + FLOAT, + INT, + UINT, + DEPTH, + STENCIL, + DEPTH_STENCIL +}; + +constexpr std::string_view to_string(TextureType type) noexcept { + switch (type) { + case TextureType::FLOAT: return "FLOAT"; + case TextureType::INT: return "INT"; + case TextureType::UINT: return "UINT"; + case TextureType::DEPTH: return "DEPTH"; + case TextureType::STENCIL: return "STENCIL"; + case TextureType::DEPTH_STENCIL: return "DEPTH_STENCIL"; + } + return "UNKNOWN"; +} + + enum class DescriptorType : uint8_t { + SAMPLER_2D_FLOAT, + SAMPLER_2D_INT, + SAMPLER_2D_UINT, + SAMPLER_2D_DEPTH, + + SAMPLER_2D_ARRAY_FLOAT, + SAMPLER_2D_ARRAY_INT, + SAMPLER_2D_ARRAY_UINT, + SAMPLER_2D_ARRAY_DEPTH, + + SAMPLER_CUBE_FLOAT, + SAMPLER_CUBE_INT, + SAMPLER_CUBE_UINT, + SAMPLER_CUBE_DEPTH, + + SAMPLER_CUBE_ARRAY_FLOAT, + SAMPLER_CUBE_ARRAY_INT, + SAMPLER_CUBE_ARRAY_UINT, + SAMPLER_CUBE_ARRAY_DEPTH, + + SAMPLER_3D_FLOAT, + SAMPLER_3D_INT, + SAMPLER_3D_UINT, + + SAMPLER_2D_MS_FLOAT, + SAMPLER_2D_MS_INT, + SAMPLER_2D_MS_UINT, + + SAMPLER_2D_MS_ARRAY_FLOAT, + SAMPLER_2D_MS_ARRAY_INT, + SAMPLER_2D_MS_ARRAY_UINT, + + SAMPLER_EXTERNAL, + UNIFORM_BUFFER, + SHADER_STORAGE_BUFFER, + INPUT_ATTACHMENT, + }; + +constexpr bool isDepthDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_DEPTH: + case DescriptorType::SAMPLER_2D_ARRAY_DEPTH: + case DescriptorType::SAMPLER_CUBE_DEPTH: + case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH: + return true; + default: ; + } + return false; +} + +constexpr bool isFloatDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_FLOAT: + case DescriptorType::SAMPLER_2D_ARRAY_FLOAT: + case DescriptorType::SAMPLER_CUBE_FLOAT: + case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT: + case DescriptorType::SAMPLER_3D_FLOAT: + case DescriptorType::SAMPLER_2D_MS_FLOAT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT: + return true; + default: ; + } + return false; +} + +constexpr bool isIntDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_INT: + case DescriptorType::SAMPLER_2D_ARRAY_INT: + case DescriptorType::SAMPLER_CUBE_INT: + case DescriptorType::SAMPLER_CUBE_ARRAY_INT: + case DescriptorType::SAMPLER_3D_INT: + case DescriptorType::SAMPLER_2D_MS_INT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_INT: + return true; + default: ; + } + return false; +} + +constexpr bool isUnsignedIntDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_UINT: + case DescriptorType::SAMPLER_2D_ARRAY_UINT: + case DescriptorType::SAMPLER_CUBE_UINT: + case DescriptorType::SAMPLER_CUBE_ARRAY_UINT: + case DescriptorType::SAMPLER_3D_UINT: + case DescriptorType::SAMPLER_2D_MS_UINT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool is3dTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_3D_FLOAT: + case DescriptorType::SAMPLER_3D_INT: + case DescriptorType::SAMPLER_3D_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool is2dTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_FLOAT: + case DescriptorType::SAMPLER_2D_INT: + case DescriptorType::SAMPLER_2D_UINT: + case DescriptorType::SAMPLER_2D_DEPTH: + case DescriptorType::SAMPLER_2D_MS_FLOAT: + case DescriptorType::SAMPLER_2D_MS_INT: + case DescriptorType::SAMPLER_2D_MS_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool is2dArrayTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_ARRAY_FLOAT: + case DescriptorType::SAMPLER_2D_ARRAY_INT: + case DescriptorType::SAMPLER_2D_ARRAY_UINT: + case DescriptorType::SAMPLER_2D_ARRAY_DEPTH: + case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_INT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: + return true; + default: ; + } + return false; +} + +constexpr bool isCubeTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_CUBE_FLOAT: + case DescriptorType::SAMPLER_CUBE_INT: + case DescriptorType::SAMPLER_CUBE_UINT: + case DescriptorType::SAMPLER_CUBE_DEPTH: + return true; + default: ; + } + return false; +} + +constexpr bool isCubeArrayTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT: + case DescriptorType::SAMPLER_CUBE_ARRAY_INT: + case DescriptorType::SAMPLER_CUBE_ARRAY_UINT: + case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH: + return true; + default: ; + } + return false; +} + +constexpr bool isMultiSampledTypeDescriptor(DescriptorType const type) noexcept { + switch (type) { + case DescriptorType::SAMPLER_2D_MS_FLOAT: + case DescriptorType::SAMPLER_2D_MS_INT: + case DescriptorType::SAMPLER_2D_MS_UINT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_INT: + case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: + return true; + default: ; + } + return false; +} + +constexpr std::string_view to_string(DescriptorType type) noexcept { + #define DESCRIPTOR_TYPE_CASE(TYPE) case DescriptorType::TYPE: return #TYPE; + switch (type) { + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_DEPTH) + DESCRIPTOR_TYPE_CASE(SAMPLER_3D_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_3D_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_3D_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_FLOAT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_INT) + DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_UINT) + DESCRIPTOR_TYPE_CASE(SAMPLER_EXTERNAL) + DESCRIPTOR_TYPE_CASE(UNIFORM_BUFFER) + DESCRIPTOR_TYPE_CASE(SHADER_STORAGE_BUFFER) + DESCRIPTOR_TYPE_CASE(INPUT_ATTACHMENT) + } + return "UNKNOWN"; + #undef DESCRIPTOR_TYPE_CASE +} + +enum class DescriptorFlags : uint8_t { + NONE = 0x00, + + // Indicate a UNIFORM_BUFFER will have dynamic offsets. + DYNAMIC_OFFSET = 0x01, + + // To indicate a texture/sampler type should be unfiltered. + UNFILTERABLE = 0x02, +}; + +using descriptor_set_t = uint8_t; + +using descriptor_binding_t = uint8_t; + +struct DescriptorSetLayoutDescriptor { + static bool isSampler(DescriptorType type) noexcept { + return int(type) <= int(DescriptorType::SAMPLER_EXTERNAL); + } + static bool isBuffer(DescriptorType type) noexcept { + return type == DescriptorType::UNIFORM_BUFFER || + type == DescriptorType::SHADER_STORAGE_BUFFER; + } + DescriptorType type; + ShaderStageFlags stageFlags; + descriptor_binding_t binding; + DescriptorFlags flags = DescriptorFlags::NONE; + uint16_t count = 0; + + friend bool operator==(DescriptorSetLayoutDescriptor const& lhs, + DescriptorSetLayoutDescriptor const& rhs) noexcept { + return lhs.type == rhs.type && + lhs.flags == rhs.flags && + lhs.count == rhs.count && + lhs.stageFlags == rhs.stageFlags; + } +}; + /** * Bitmask for selecting render buffers */ @@ -207,7 +544,7 @@ enum class TargetBufferFlags : uint32_t { ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected. }; -inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept { +constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept { if (index == 0u) return TargetBufferFlags::COLOR0; if (index == 1u) return TargetBufferFlags::COLOR1; if (index == 2u) return TargetBufferFlags::COLOR2; @@ -222,12 +559,21 @@ inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept } /** - * Frequency at which a buffer is expected to be modified and used. This is used as an hint - * for the driver to make better decisions about managing memory internally. + * How the buffer will be used. */ enum class BufferUsage : uint8_t { - STATIC, //!< content modified once, used many times - DYNAMIC, //!< content modified frequently, used many times + STATIC = 0, //!< (legacy) content modified once, used many times + DYNAMIC = 1, //!< (legacy) content modified frequently, used many times + DYNAMIC_BIT = 0x1, //!< buffer can be modified frequently, used many times + SHARED_WRITE_BIT = 0x04, //!< buffer can be memory mapped for write operations +}; + +/** + * How the buffer will be mapped. + */ +enum class MapBufferAccessFlags : uint8_t { + WRITE_BIT = 0x2, //!< buffer is mapped from writing + INVALIDATE_RANGE_BIT = 0x4, //!< the mapped range content is lost }; /** @@ -243,8 +589,19 @@ struct Viewport { int32_t right() const noexcept { return left + int32_t(width); } //! get the top coordinate in window space of the viewport int32_t top() const noexcept { return bottom + int32_t(height); } -}; + friend bool operator==(Viewport const& lhs, Viewport const& rhs) noexcept { + // clang can do this branchless with xor/or + return lhs.left == rhs.left && lhs.bottom == rhs.bottom && + lhs.width == rhs.width && lhs.height == rhs.height; + } + + friend bool operator!=(Viewport const& lhs, Viewport const& rhs) noexcept { + // clang is being dumb and uses branches + return bool(((lhs.left ^ rhs.left) | (lhs.bottom ^ rhs.bottom)) | + ((lhs.width ^ rhs.width) | (lhs.height ^ rhs.height))); + } +}; /** * Specifies the mapping of the near and far clipping plane to window coordinates. @@ -264,15 +621,6 @@ enum class FenceStatus : int8_t { TIMEOUT_EXPIRED = 1, //!< wait()'s timeout expired. The Fence condition is not satisfied. }; -/** - * Status codes for sync objects - */ -enum class SyncStatus : int8_t { - ERROR = -1, //!< An error occurred. The Sync is not signaled. - SIGNALED = 0, //!< The Sync is signaled. - NOT_SIGNALED = 1, //!< The Sync is not signaled yet -}; - static constexpr uint64_t FENCE_WAIT_FOR_EVER = uint64_t(-1); /** @@ -291,6 +639,15 @@ enum class ShaderModel : uint8_t { }; static constexpr size_t SHADER_MODEL_COUNT = 2; +constexpr std::string_view to_string(ShaderModel model) noexcept { + switch (model) { + case ShaderModel::MOBILE: + return "mobile"; + case ShaderModel::DESKTOP: + return "desktop"; + } +} + /** * Primitive types */ @@ -303,6 +660,18 @@ enum class PrimitiveType : uint8_t { TRIANGLE_STRIP = 5 //!< triangle strip }; +[[nodiscard]] constexpr bool isStripPrimitiveType(const PrimitiveType type) { + switch (type) { + case PrimitiveType::POINTS: + case PrimitiveType::LINES: + case PrimitiveType::TRIANGLES: + return false; + case PrimitiveType::LINE_STRIP: + case PrimitiveType::TRIANGLE_STRIP: + return true; + } +} + /** * Supported uniform types */ @@ -331,7 +700,7 @@ enum class UniformType : uint8_t { /** * Supported constant parameter types */ - enum class ConstantType : uint8_t { +enum class ConstantType : uint8_t { INT, FLOAT, BOOL @@ -344,11 +713,29 @@ enum class Precision : uint8_t { DEFAULT }; +union ConstantValue { + int32_t i; + float f; + bool b; +}; + /** * Shader compiler priority queue + * + * On platforms which support parallel shader compilation, compilation requests will be processed in + * order of priority, then insertion order. See Material::compile(). */ enum class CompilerPriorityQueue : uint8_t { + /** We need this program NOW. + * + * When passed as an argument to Material::compile(), if the platform doesn't support parallel + * compilation, but does support amortized shader compilation, the given shader program will be + * synchronously compiled. + */ + CRITICAL, + /** We will need this program soon. */ HIGH, + /** We will need this program eventually. */ LOW }; @@ -362,6 +749,24 @@ enum class SamplerType : uint8_t { SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2) }; +constexpr std::string_view to_string(SamplerType const type) noexcept { + switch (type) { + case SamplerType::SAMPLER_2D: + return "SAMPLER_2D"; + case SamplerType::SAMPLER_2D_ARRAY: + return "SAMPLER_2D_ARRAY"; + case SamplerType::SAMPLER_CUBEMAP: + return "SAMPLER_CUBEMAP"; + case SamplerType::SAMPLER_EXTERNAL: + return "SAMPLER_EXTERNAL"; + case SamplerType::SAMPLER_3D: + return "SAMPLER_3D"; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: + return "SAMPLER_CUBEMAP_ARRAY"; + } + return "Unknown"; +} + //! Subpass type enum class SubpassType : uint8_t { SUBPASS_INPUT @@ -375,6 +780,20 @@ enum class SamplerFormat : uint8_t { SHADOW = 3 //!< shadow sampler (PCF) }; +constexpr std::string_view to_string(SamplerFormat const format) noexcept { + switch (format) { + case SamplerFormat::INT: + return "INT"; + case SamplerFormat::UINT: + return "UINT"; + case SamplerFormat::FLOAT: + return "FLOAT"; + case SamplerFormat::SHADOW: + return "SHADOW"; + } + return "Unknown"; +} + /** * Supported element types */ @@ -414,6 +833,15 @@ enum class BufferObjectBinding : uint8_t { SHADER_STORAGE }; +constexpr std::string_view to_string(BufferObjectBinding type) noexcept { + switch (type) { + case BufferObjectBinding::VERTEX: return "VERTEX"; + case BufferObjectBinding::UNIFORM: return "UNIFORM"; + case BufferObjectBinding::SHADER_STORAGE: return "SHADER_STORAGE"; + } + return "UNKNOWN"; +} + //! Face culling Mode enum class CullingMode : uint8_t { NONE, //!< No culling, front and back faces are visible @@ -675,6 +1103,8 @@ enum class TextureFormat : uint16_t { SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB }; +TextureType getTextureType(TextureFormat format) noexcept; + //! Bitmask describing the intended Texture Usage enum class TextureUsage : uint16_t { NONE = 0x0000, @@ -686,8 +1116,10 @@ enum class TextureUsage : uint16_t { SUBPASS_INPUT = 0x0020, //!< Texture can be used as a subpass input BLIT_SRC = 0x0040, //!< Texture can be used the source of a blit() BLIT_DST = 0x0080, //!< Texture can be used the destination of a blit() - PROTECTED = 0x0100, //!< Texture can be used the destination of a blit() - DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage + PROTECTED = 0x0100, //!< Texture can be used for protected content + GEN_MIPMAPPABLE = 0x0200, //!< Texture can be used with generateMipmaps() + DEFAULT = UPLOADABLE | SAMPLEABLE, //!< Default texture usage + ALL_ATTACHMENTS = COLOR_ATTACHMENT | DEPTH_ATTACHMENT | STENCIL_ATTACHMENT | SUBPASS_INPUT, //!< Mask of all attachments }; //! Texture swizzle @@ -701,7 +1133,7 @@ enum class TextureSwizzle : uint8_t { }; //! returns whether this format a depth format -static constexpr bool isDepthFormat(TextureFormat format) noexcept { +constexpr bool isDepthFormat(TextureFormat format) noexcept { switch (format) { case TextureFormat::DEPTH32F: case TextureFormat::DEPTH24: @@ -714,7 +1146,7 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept { } } -static constexpr bool isStencilFormat(TextureFormat format) noexcept { +constexpr bool isStencilFormat(TextureFormat format) noexcept { switch (format) { case TextureFormat::STENCIL8: case TextureFormat::DEPTH24_STENCIL8: @@ -725,7 +1157,34 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept { } } -static constexpr bool isUnsignedIntFormat(TextureFormat format) { +constexpr bool isColorFormat(TextureFormat format) noexcept { + switch (format) { + // Standard color formats + case TextureFormat::R8: + case TextureFormat::RG8: + case TextureFormat::RGBA8: + case TextureFormat::R16F: + case TextureFormat::RG16F: + case TextureFormat::RGBA16F: + case TextureFormat::R32F: + case TextureFormat::RG32F: + case TextureFormat::RGBA32F: + case TextureFormat::RGB10_A2: + case TextureFormat::R11F_G11F_B10F: + case TextureFormat::SRGB8: + case TextureFormat::SRGB8_A8: + case TextureFormat::RGB8: + case TextureFormat::RGB565: + case TextureFormat::RGB5_A1: + case TextureFormat::RGBA4: + return true; + default: + break; + } + return false; +} + +constexpr bool isUnsignedIntFormat(TextureFormat format) { switch (format) { case TextureFormat::R8UI: case TextureFormat::R16UI: @@ -746,7 +1205,7 @@ static constexpr bool isUnsignedIntFormat(TextureFormat format) { } } -static constexpr bool isSignedIntFormat(TextureFormat format) { +constexpr bool isSignedIntFormat(TextureFormat format) { switch (format) { case TextureFormat::R8I: case TextureFormat::R16I: @@ -768,35 +1227,35 @@ static constexpr bool isSignedIntFormat(TextureFormat format) { } //! returns whether this format is a compressed format -static constexpr bool isCompressedFormat(TextureFormat format) noexcept { +constexpr bool isCompressedFormat(TextureFormat format) noexcept { return format >= TextureFormat::EAC_R11; } //! returns whether this format is an ETC2 compressed format -static constexpr bool isETC2Compression(TextureFormat format) noexcept { +constexpr bool isETC2Compression(TextureFormat format) noexcept { return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8; } //! returns whether this format is an S3TC compressed format -static constexpr bool isS3TCCompression(TextureFormat format) noexcept { +constexpr bool isS3TCCompression(TextureFormat format) noexcept { return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA; } -static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept { +constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept { return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA; } //! returns whether this format is an RGTC compressed format -static constexpr bool isRGTCCompression(TextureFormat format) noexcept { +constexpr bool isRGTCCompression(TextureFormat format) noexcept { return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2; } //! returns whether this format is an BPTC compressed format -static constexpr bool isBPTCCompression(TextureFormat format) noexcept { +constexpr bool isBPTCCompression(TextureFormat format) noexcept { return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM; } -static constexpr bool isASTCCompression(TextureFormat format) noexcept { +constexpr bool isASTCCompression(TextureFormat format) noexcept { return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12; } @@ -857,7 +1316,7 @@ enum class SamplerCompareFunc : uint8_t { }; //! Sampler parameters -struct SamplerParams { // NOLINT +struct SamplerParams { // NOLINT SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST) SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST) SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE) @@ -881,6 +1340,9 @@ struct SamplerParams { // NOLINT struct EqualTo { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { + assert_invariant(lhs.padding0 == 0); + assert_invariant(lhs.padding1 == 0); + assert_invariant(lhs.padding2 == 0); auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); return *pLhs == *pRhs; @@ -889,17 +1351,31 @@ struct SamplerParams { // NOLINT struct LessThan { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { + assert_invariant(lhs.padding0 == 0); + assert_invariant(lhs.padding1 == 0); + assert_invariant(lhs.padding2 == 0); auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); - return *pLhs == *pRhs; + return *pLhs < *pRhs; } }; + bool isFiltered() const noexcept { + return filterMag != SamplerMagFilter::NEAREST || filterMin != SamplerMinFilter::NEAREST; + } + private: - friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept { - return SamplerParams::LessThan{}(lhs, rhs); + friend bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept { + return EqualTo{}(lhs, rhs); + } + friend bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept { + return !EqualTo{}(lhs, rhs); + } + friend bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept { + return LessThan{}(lhs, rhs); } }; + static_assert(sizeof(SamplerParams) == 4); // The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code @@ -907,6 +1383,11 @@ static_assert(sizeof(SamplerParams) == 4); static_assert(sizeof(SamplerParams) <= sizeof(uint64_t), "SamplerParams must be no more than 64 bits"); +struct DescriptorSetLayout { + std::variant label; + utils::FixedCapacityVector descriptors; +}; + //! blending equation function enum class BlendEquation : uint8_t { ADD, //!< the fragment is added to the color buffer @@ -1052,7 +1533,7 @@ struct RasterState { bool inverseFrontFaces : 1; // 31 //! padding, must be 0 - uint8_t padding : 1; // 32 + bool depthClamp : 1; // 32 }; uint32_t u = 0; }; @@ -1063,32 +1544,6 @@ struct RasterState { * \privatesection */ -enum class ShaderStage : uint8_t { - VERTEX = 0, - FRAGMENT = 1, - COMPUTE = 2 -}; - -static constexpr size_t PIPELINE_STAGE_COUNT = 2; -enum class ShaderStageFlags : uint8_t { - NONE = 0, - VERTEX = 0x1, - FRAGMENT = 0x2, - COMPUTE = 0x4, - ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE -}; - -static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { - switch (type) { - case ShaderStage::VERTEX: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); - case ShaderStage::FRAGMENT: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT)); - case ShaderStage::COMPUTE: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE)); - } -} - /** * Selects which buffers to clear at the beginning of the render pass, as well as which buffers * can be discarded at the beginning and end of the render pass. @@ -1218,13 +1673,15 @@ struct StencilState { uint8_t padding = 0; }; +using PushConstantVariant = std::variant; + static_assert(sizeof(StencilState::StencilOperations) == 5u, "StencilOperations size not what was intended"); static_assert(sizeof(StencilState) == 12u, "StencilState size not what was intended"); -using FrameScheduledCallback = void(*)(PresentCallable callable, void* user); +using FrameScheduledCallback = utils::Invocable; enum class Workaround : uint16_t { // The EASU pass must split because shader compiler flattens early-exit branch @@ -1235,21 +1692,34 @@ enum class Workaround : uint16_t { // for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu ADRENO_UNIFORM_ARRAY_CRASH, // Workaround a Metal pipeline compilation error with the message: - // "Could not statically determine the target of a texture". See light_indirect.fs - A8X_STATIC_TEXTURE_TARGET_ERROR, + // "Could not statically determine the target of a texture". See surface_light_indirect.fs + METAL_STATIC_TEXTURE_TARGET_ERROR, // Adreno drivers sometimes aren't able to blit into a layer of a texture array. DISABLE_BLIT_INTO_TEXTURE_ARRAY, // Multiple workarounds needed for PowerVR GPUs POWER_VR_SHADER_WORKAROUNDS, + // Some browsers, such as Firefox on Mac, struggle with slow shader compile/link times when + // creating programs for the default material, leading to startup stutters. This workaround + // prevents these stutters by not precaching depth variants of the default material for those + // particular browsers. + DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL, + // Emulate an sRGB swapchain in shader code. + EMULATE_SRGB_SWAPCHAIN, }; -//! The type of technique for stereoscopic rendering -enum class StereoscopicType : uint8_t { - // Stereoscopic rendering is performed using instanced rendering technique. - INSTANCED, - // Stereoscopic rendering is performed using the multiview feature from the graphics backend. - MULTIVIEW, -}; +using StereoscopicType = Platform::StereoscopicType; + +using FrameTimestamps = Platform::FrameTimestamps; + +using CompositorTiming = Platform::CompositorTiming; + +using AsynchronousMode = Platform::AsynchronousMode; + +using AsyncCallId = uint32_t; + +static constexpr AsyncCallId InvalidAsyncCallId = std::numeric_limits::max(); + +using AsynchronousMode = Platform::AsynchronousMode; } // namespace filament::backend @@ -1257,10 +1727,17 @@ template<> struct utils::EnableBitMaskOperators struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; template<> struct utils::EnableBitMaskOperators : public std::true_type {}; template<> struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; + template<> struct utils::EnableIntegerOperators : public std::true_type {}; template<> struct utils::EnableIntegerOperators @@ -1289,12 +1766,16 @@ utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::Textu utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::TextureUsage usage); utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::BufferObjectBinding binding); utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::TextureSwizzle swizzle); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStage shaderStage); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStageFlags stageFlags); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::CompilerPriorityQueue compilerPriorityQueue); +utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::PushConstantVariant pushConstantVariant); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::AttributeArray& type); +utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::DescriptorSetLayout& dsl); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::PolygonOffset& po); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RasterState& rs); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::RenderPassParams& b); utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::Viewport& v); -utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::ShaderStageFlags stageFlags); #endif #endif // TNT_FILAMENT_BACKEND_DRIVERENUMS_H diff --git a/package/ios/libs/filament/include/backend/Handle.h b/package/ios/libs/filament/include/backend/Handle.h index 4b63607a..c9b123c0 100644 --- a/package/ios/libs/filament/include/backend/Handle.h +++ b/package/ios/libs/filament/include/backend/Handle.h @@ -17,15 +17,17 @@ #ifndef TNT_FILAMENT_BACKEND_HANDLE_H #define TNT_FILAMENT_BACKEND_HANDLE_H -#if !defined(NDEBUG) -#include -#endif #include #include // FIXME: STL headers are not allowed in public headers +#include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { struct HwBufferObject; @@ -34,13 +36,16 @@ struct HwIndexBuffer; struct HwProgram; struct HwRenderPrimitive; struct HwRenderTarget; -struct HwSamplerGroup; struct HwStream; struct HwSwapChain; +struct HwSync; struct HwTexture; struct HwTimerQuery; struct HwVertexBufferInfo; struct HwVertexBuffer; +struct HwDescriptorSetLayout; +struct HwDescriptorSet; +struct HwMemoryMappedBuffer; /* * A handle to a backend resource. HandleBase is for internal use only. @@ -75,6 +80,19 @@ class HandleBase { HandleBase(HandleBase const& rhs) noexcept = default; HandleBase& operator=(HandleBase const& rhs) noexcept = default; + HandleBase(HandleBase&& rhs) noexcept + : object(rhs.object) { + rhs.object = nullid; + } + + HandleBase& operator=(HandleBase&& rhs) noexcept { + if (this != &rhs) { + object = rhs.object; + rhs.object = nullid; + } + return *this; + } + private: HandleId object; }; @@ -89,8 +107,20 @@ struct Handle : public HandleBase { Handle() noexcept = default; Handle(Handle const& rhs) noexcept = default; - - Handle& operator=(Handle const& rhs) noexcept = default; + Handle(Handle&& rhs) noexcept = default; + + // Explicitly redefine copy/move assignment operators rather than just using default here. + // Because it doesn't make a call to the parent's method automatically during the std::move + // function call(https://en.cppreference.com/w/cpp/algorithm/move) in certain compilers like + // NDK 25.1.8937393 and below (see b/371980551) + Handle& operator=(Handle const& rhs) noexcept { + HandleBase::operator=(rhs); + return *this; + } + Handle& operator=(Handle&& rhs) noexcept { + HandleBase::operator=(std::move(rhs)); + return *this; + } explicit Handle(HandleId id) noexcept : HandleBase(id) { } @@ -103,7 +133,7 @@ struct Handle : public HandleBase { bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); } // type-safe Handle cast - template::value> > + template> > Handle(Handle const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor) private: @@ -115,19 +145,22 @@ struct Handle : public HandleBase { // Types used by the command stream // (we use this renaming because the macro-system doesn't deal well with "<" and ">") -using BufferObjectHandle = Handle; -using FenceHandle = Handle; -using IndexBufferHandle = Handle; -using ProgramHandle = Handle; -using RenderPrimitiveHandle = Handle; -using RenderTargetHandle = Handle; -using SamplerGroupHandle = Handle; -using StreamHandle = Handle; -using SwapChainHandle = Handle; -using TextureHandle = Handle; -using TimerQueryHandle = Handle; -using VertexBufferHandle = Handle; -using VertexBufferInfoHandle = Handle; +using BufferObjectHandle = Handle; +using FenceHandle = Handle; +using IndexBufferHandle = Handle; +using ProgramHandle = Handle; +using RenderPrimitiveHandle = Handle; +using RenderTargetHandle = Handle; +using StreamHandle = Handle; +using SwapChainHandle = Handle; +using SyncHandle = Handle; +using TextureHandle = Handle; +using TimerQueryHandle = Handle; +using VertexBufferHandle = Handle; +using VertexBufferInfoHandle = Handle; +using DescriptorSetLayoutHandle = Handle; +using DescriptorSetHandle = Handle; +using MemoryMappedBufferHandle = Handle; } // namespace filament::backend diff --git a/package/ios/libs/filament/include/backend/PipelineState.h b/package/ios/libs/filament/include/backend/PipelineState.h index 220d04bb..106e579e 100644 --- a/package/ios/libs/filament/include/backend/PipelineState.h +++ b/package/ios/libs/filament/include/backend/PipelineState.h @@ -20,17 +20,27 @@ #include #include -#include +#include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { //! \privatesection +struct PipelineLayout { + using SetLayout = std::array, MAX_DESCRIPTOR_SET_COUNT>; + SetLayout setLayout; // 16 +}; + struct PipelineState { Handle program; // 4 Handle vertexBufferInfo; // 4 + PipelineLayout pipelineLayout; // 16 RasterState rasterState; // 4 StencilState stencilState; // 12 PolygonOffset polygonOffset; // 8 diff --git a/package/ios/libs/filament/include/backend/PixelBufferDescriptor.h b/package/ios/libs/filament/include/backend/PixelBufferDescriptor.h index c45f344d..93f82c9d 100644 --- a/package/ios/libs/filament/include/backend/PixelBufferDescriptor.h +++ b/package/ios/libs/filament/include/backend/PixelBufferDescriptor.h @@ -24,11 +24,14 @@ #include #include -#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { /** @@ -201,23 +204,15 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { }, new T(std::forward(functor)) }; } - - // -------------------------------------------------------------------------------------------- - /** - * Computes the size in bytes needed to fit an image of given dimensions and format + * Computes the size in bytes for a pixel of given dimensions and format * * @param format Format of the image pixels * @param type Type of the image pixels - * @param stride Stride of a row in pixels - * @param height Height of the image in rows - * @param alignment Alignment in bytes of pixel rows - * @return The buffer size needed to fit this image in bytes + * @return The size of the specified pixel in bytes */ - static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type, - size_t stride, size_t height, size_t alignment) noexcept { - assert_invariant(alignment); + static constexpr size_t computePixelSize(PixelDataFormat format, PixelDataType type) noexcept { if (type == PixelDataType::COMPRESSED) { return 0; } @@ -239,7 +234,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { case PixelDataFormat::RGB_INTEGER: n = 3; break; - case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm) + case PixelDataFormat::UNUSED:// shouldn't happen (used to be rgbm) case PixelDataFormat::RGBA: case PixelDataFormat::RGBA_INTEGER: n = 4; @@ -248,7 +243,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { size_t bpp = n; switch (type) { - case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings + case PixelDataType::COMPRESSED:// Impossible -- to squash the IDE warnings case PixelDataType::UBYTE: case PixelDataType::BYTE: // nothing to do @@ -279,16 +274,35 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { bpp = 2; break; } + return bpp; + } + + // -------------------------------------------------------------------------------------------- + + /** + * Computes the size in bytes needed to fit an image of given dimensions and format + * + * @param format Format of the image pixels + * @param type Type of the image pixels + * @param stride Stride of a row in pixels + * @param height Height of the image in rows + * @param alignment Alignment in bytes of pixel rows + * @return The buffer size needed to fit this image in bytes + */ + static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type, + size_t stride, size_t height, size_t alignment) noexcept { + assert_invariant(alignment); + size_t bpp = computePixelSize(format, type); size_t const bpr = bpp * stride; size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1); return bprAligned * height; } //! left coordinate in pixels - uint32_t left = 0; + uint32_t left = 0; //! top coordinate in pixels - uint32_t top = 0; + uint32_t top = 0; union { struct { //! stride in pixels diff --git a/package/ios/libs/filament/include/backend/Platform.h b/package/ios/libs/filament/include/backend/Platform.h index 03026dff..dcbc9928 100644 --- a/package/ios/libs/filament/include/backend/Platform.h +++ b/package/ios/libs/filament/include/backend/Platform.h @@ -19,14 +19,21 @@ #ifndef TNT_FILAMENT_BACKEND_PLATFORM_H #define TNT_FILAMENT_BACKEND_PLATFORM_H +#include #include #include +#include #include #include +#include +#include +#include + namespace filament::backend { +class CallbackHandler; class Driver; /** @@ -40,6 +47,235 @@ class UTILS_PUBLIC Platform { struct SwapChain {}; struct Fence {}; struct Stream {}; + struct Sync {}; + + using SyncCallback = void(*)(Sync* UTILS_NONNULL sync, void* UTILS_NULLABLE userData); + + class ExternalImageHandle; + + class ExternalImage { + friend class ExternalImageHandle; + std::atomic_uint32_t mRefCount{0}; + protected: + virtual ~ExternalImage() noexcept; + }; + + class ExternalImageHandle { + ExternalImage* UTILS_NULLABLE mTarget = nullptr; + static void incref(ExternalImage* UTILS_NULLABLE p) noexcept; + static void decref(ExternalImage* UTILS_NULLABLE p) noexcept; + + public: + ExternalImageHandle() noexcept; + ~ExternalImageHandle() noexcept; + explicit ExternalImageHandle(ExternalImage* UTILS_NULLABLE p) noexcept; + ExternalImageHandle(ExternalImageHandle const& rhs) noexcept; + ExternalImageHandle(ExternalImageHandle&& rhs) noexcept; + ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept; + ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept; + + bool operator==(const ExternalImageHandle& rhs) const noexcept { + return mTarget == rhs.mTarget; + } + explicit operator bool() const noexcept { return mTarget != nullptr; } + + ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; } + ExternalImage const* UTILS_NULLABLE get() const noexcept { return mTarget; } + + ExternalImage* UTILS_NULLABLE operator->() noexcept { return mTarget; } + ExternalImage const* UTILS_NULLABLE operator->() const noexcept { return mTarget; } + + ExternalImage& operator*() noexcept { return *mTarget; } + ExternalImage const& operator*() const noexcept { return *mTarget; } + + void clear() noexcept; + void reset(ExternalImage* UTILS_NULLABLE p) noexcept; + + private: + friend utils::io::ostream& operator<<(utils::io::ostream& out, + ExternalImageHandle const& handle); + }; + + using ExternalImageHandleRef = ExternalImageHandle const&; + + struct CompositorTiming { + /** duration in nanosecond since epoch of std::steady_clock */ + using time_point_ns = int64_t; + /** duration in nanosecond on the std::steady_clock */ + using duration_ns = int64_t; + static constexpr time_point_ns INVALID = -1; //!< value not supported + /** + * The timestamp [ns] since epoch of the next time the compositor will begin composition. + * This is effectively the deadline for when the compositor must receive a newly queued + * frame. + */ + time_point_ns compositeDeadline; + + /** + * The time delta [ns] between subsequent composition events. + */ + duration_ns compositeInterval; + + /** + * The time delta [ns] between the start of composition and the expected present time of + * that composition. This can be used to estimate the latency of the actual present time. + */ + duration_ns compositeToPresentLatency; + + /** + * The timestamp [ns] since epoch of the system's expected presentation time. + * INVALID if not supported. + */ + time_point_ns expectedPresentTime; + + /** + * The timestamp [ns] since epoch of the current frame's start (i.e. vsync) + * INVALID if not supported. + */ + time_point_ns frameTime; + + /** + * The timestamp [ns] since epoch of the current frame's deadline + * INVALID if not supported. + */ + time_point_ns frameTimelineDeadline; + }; + + struct FrameTimestamps { + /** duration in nanosecond since epoch of std::steady_clock */ + using time_point_ns = int64_t; + static constexpr time_point_ns INVALID = -1; //!< value not supported + static constexpr time_point_ns PENDING = -2; //!< value not yet available + + /** + * The time the application requested this frame be presented. + * If the application does not request a presentation time explicitly, + * this will correspond to buffer's queue time. + */ + time_point_ns requestedPresentTime; + + /** + * The time when all the application's rendering to the surface was completed. + */ + time_point_ns acquireTime; + + /** + * The time when the compositor selected this frame as the one to use for the next + * composition. This is the earliest indication that the frame was submitted in time. + */ + time_point_ns latchTime; + + /** + * The first time at which the compositor began preparing composition for this frame. + * Zero if composition was handled by the display and the compositor didn't do any + * rendering. + */ + time_point_ns firstCompositionStartTime; + + /** + * The last time at which the compositor began preparing composition for this frame, for + * frames composited more than once. Zero if composition was handled by the display and the + * compositor didn't do any rendering. + */ + time_point_ns lastCompositionStartTime; + + /** + * The time at which the compositor's rendering work for this frame finished. This will be + * INVALID if composition was handled by the display and the compositor didn't do any + * rendering. + */ + time_point_ns gpuCompositionDoneTime; + + /** + * The time at which this frame started to scan out to the physical display. + */ + time_point_ns displayPresentTime; + + /** + * The time when the buffer became available for reuse as a buffer the client can target + * without blocking. This is generally the point when all read commands of the buffer have + * been submitted, but not necessarily completed. + */ + time_point_ns dequeueReadyTime; + + /** + * The time at which all reads for the purpose of display/composition were completed for + * this frame. + */ + time_point_ns releaseTime; + }; + + /** + * The type of technique for stereoscopic rendering. (Note that the materials used will need to + * be compatible with the chosen technique.) + */ + enum class StereoscopicType : uint8_t { + /** + * No stereoscopic rendering + */ + NONE, + /** + * Stereoscopic rendering is performed using instanced rendering technique. + */ + INSTANCED, + /** + * Stereoscopic rendering is performed using the multiview feature from the graphics + * backend. + */ + MULTIVIEW, + }; + + /** + * This controls the priority level for GPU work scheduling, which helps prioritize the + * submitted GPU work and enables preemption. + */ + enum class GpuContextPriority : uint8_t { + /** + * Backend default GPU context priority (typically MEDIUM) + */ + DEFAULT, + /** + * For non-interactive, deferrable workloads. This should not interfere with standard + * applications. + */ + LOW, + /** + * The default priority level for standard applications. + */ + MEDIUM, + /** + * For high-priority, latency-sensitive workloads that are more important than standard + * applications. + */ + HIGH, + /** + * The highest priority, intended for system-critical, real-time applications where missing + * deadlines is unacceptable (e.g., VR/AR compositors or other system-critical tasks). + */ + REALTIME, + }; + + /** + * Defines how asynchronous operations are handled by the engine. + */ + enum class AsynchronousMode : uint8_t { + /** + * Asynchronous operations are disabled. This is the default. + */ + NONE, + + /** + * Attempts to use a dedicated worker thread for asynchronous tasks. If threading is not + * supported by the platform, it automatically falls back to using an amortization strategy. + */ + THREAD_PREFERRED, + + /** + * Uses an amortization strategy, processing a small number of asynchronous tasks during + * each engine update cycle. + */ + AMORTIZATION, + }; struct DriverConfig { /** @@ -48,23 +284,85 @@ class UTILS_PUBLIC Platform { */ size_t handleArenaSize = 0; - /** - * This number of most-recently destroyed textures will be tracked for use-after-free. - * Throws an exception when a texture is freed but still bound to a SamplerGroup and used in - * a draw call. 0 disables completely. Currently only respected by the Metal backend. - */ - size_t textureUseAfterFreePoolSize = 0; + size_t metalUploadBufferSizeBytes = 512 * 1024; /** * Set to `true` to forcibly disable parallel shader compilation in the backend. - * Currently only honored by the GL and Metal backends. + * Currently only honored by the GL and Metal backends, and the Vulkan backend + * when some experimental features are enabled. */ bool disableParallelShaderCompile = false; + /** + * Set to `true` to forcibly disable amortized shader compilation in the backend. + * Currently only honored by the GL backend. + */ + bool disableAmortizedShaderCompile = true; + /** * Disable backend handles use-after-free checks. */ bool disableHandleUseAfterFreeCheck = false; + + /** + * Disable backend handles tags for heap allocated (fallback) handles + */ + bool disableHeapHandleTags = false; + + /** + * Force GLES2 context if supported, or pretend the context is ES2. Only meaningful on + * GLES 3.x backends. + */ + bool forceGLES2Context = false; + + /** + * Sets the technique for stereoscopic rendering. + */ + StereoscopicType stereoscopicType = StereoscopicType::NONE; + + /** + * Assert the native window associated to a SwapChain is valid when calling makeCurrent(). + * This is only supported for: + * - PlatformEGLAndroid + */ + bool assertNativeWindowIsValid = false; + + /** + * The action to take if a Drawable cannot be acquired. If true, the + * frame is aborted instead of panic. This is only supported for: + * - PlatformMetal + */ + bool metalDisablePanicOnDrawableFailure = false; + + /** + * GPU context priority level. Controls GPU work scheduling and preemption. + * This is only supported for: + * - PlatformEGL + */ + GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT; + + /** + * Enables asynchronous pipeline cache preloading, if supported on this device. + * This is only supported for: + * - VulkanPlatform + * When the following device extensions are available: + * - VK_KHR_dynamic_rendering + * - VK_EXT_vertex_input_dynamic_state + * Should be enabled only for devices where it has been shown this is effective. + */ + bool vulkanEnableAsyncPipelineCachePrewarming = false; + + /** + * Bypass the staging buffer because the device is of Unified Memory Architecture. + * This is only supported for: + * - VulkanPlatform + */ + bool vulkanEnableStagingBufferBypass = false; + + /** + * Asynchronous mode for the engine. Defines how asynchronous operations are handled. + */ + AsynchronousMode asynchronousMode = AsynchronousMode::NONE; }; Platform() noexcept; @@ -85,13 +383,13 @@ class UTILS_PUBLIC Platform { * @param sharedContext an optional shared context. This is not meaningful with all graphic * APIs and platforms. * For EGL platforms, this is an EGLContext. - * + * * @param driverConfig specifies driver initialization parameters * * @return nullptr on failure, or a pointer to the newly created driver. */ - virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext, - const DriverConfig& driverConfig) noexcept = 0; + virtual Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext, + const DriverConfig& driverConfig) = 0; /** * Processes the platform's event queue when called from its primary event-handling thread. @@ -102,6 +400,65 @@ class UTILS_PUBLIC Platform { */ virtual bool pumpEvents() noexcept; + // -------------------------------------------------------------------------------------------- + // Swapchain timing APIs + + /** + * Whether this platform supports compositor timing querying. + * + * @return true if this Platform supports compositor timings, false otherwise [default] + * @see queryCompositorTiming() + * @see setPresentFrameId() + * @see queryFrameTimestamps() + */ + virtual bool isCompositorTimingSupported() const noexcept; + + /** + * If compositor timing is supported, fills the provided CompositorTiming structure + * with timing information form the compositor the swapchain's native window is using. + * The swapchain'snative window must be valid (i.e. not a headless swapchain). + * @param swapchain to query the compositor timing from + * @return true on success, false otherwise (e.g. if not supported) + * @see isCompositorTimingSupported() + */ + virtual bool queryCompositorTiming(SwapChain const* UTILS_NONNULL swapchain, + CompositorTiming* UTILS_NONNULL outCompositorTiming) const noexcept; + + /** + * Associate a generic frameId which must be monotonically increasing (albeit not strictly) with + * the next frame to be presented on the specified swapchain. + * + * This must be called from the backend thread. + * + * @param swapchain + * @param frameId + * @return true on success, false otherwise + * @see isCompositorTimingSupported() + * @see queryFrameTimestamps() + */ + virtual bool setPresentFrameId(SwapChain const* UTILS_NONNULL swapchain, + uint64_t frameId) noexcept; + + /** + * If compositor timing is supported, fills the provided FrameTimestamps structure + * with timing information of a given frame, identified by the frame id, of the specified + * swapchain. The system only keeps a limited history of frames timings. + * + * This API is thread safe and can be called from any thread. + * + * @param swapchain swapchain to query the timestamps of + * @param frameId frame we're interested it + * @param outFrameTimestamps output structure receiving the timestamps + * @return true if successful, false otherwise + * @see isCompositorTimingSupported() + * @see setPresentFrameId() + */ + virtual bool queryFrameTimestamps(SwapChain const* UTILS_NONNULL swapchain, + uint64_t frameId, FrameTimestamps* UTILS_NONNULL outFrameTimestamps) const noexcept; + + // -------------------------------------------------------------------------------------------- + // Caching APIs + /** * InsertBlobFunc is an Invocable to an application-provided function that a * backend implementation may use to insert a key/value pair into the @@ -194,13 +551,25 @@ class UTILS_PUBLIC Platform { size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize, void* UTILS_NONNULL value, size_t valueSize); - using DebugUpdateStatFunc = utils::Invocable; + // -------------------------------------------------------------------------------------------- + // Debugging APIs + + using DebugUpdateStatFunc = utils::Invocable; /** * Sets the callback function that the backend can use to update backend-specific statistics * to aid with debugging. This callback is guaranteed to be called on the Filament driver * thread. * + * The callback signature is (key, intValue, stringValue). Note that for any given call, + * only one of the value parameters (intValue or stringValue) will be meaningful, depending on + * the specific key. + * + * IMPORTANT_NOTE: because the callback is called on the driver thread, only quick, non-blocking + * work should be done inside it. Furthermore, no graphics API calls (such as GL calls) should + * be made, which could interfere with Filament's driver state. + * * @param debugUpdateStat an Invocable that updates debug statistics */ void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept; @@ -219,15 +588,32 @@ class UTILS_PUBLIC Platform { * This function is guaranteed to be called only on a single thread, the Filament driver * thread. * - * @param key a null-terminated C-string with the key of the debug statistic - * @param value the updated value of key + * @param key a null-terminated C-string with the key of the debug statistic + * @param intValue the updated integer value of key (the string value passed to the + * callback will be empty) + */ + void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue); + + /** + * To track backend-specific statistics, the backend implementation can call the + * application-provided callback function debugUpdateStatFunc to associate or update a value + * with a given key. It is possible for this function to be called multiple times with the + * same key, in which case newer values should overwrite older values. + * + * This function is guaranteed to be called only on a single thread, the Filament driver + * thread. + * + * @param key a null-terminated C-string with the key of the debug statistic + * @param stringValue the updated string value of key (the integer value passed to the + * callback will be 0) */ - void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value); + void debugUpdateStat(const char* UTILS_NONNULL key, utils::CString stringValue); private: - InsertBlobFunc mInsertBlob; - RetrieveBlobFunc mRetrieveBlob; - DebugUpdateStatFunc mDebugUpdateStat; + std::shared_ptr mInsertBlob; + std::shared_ptr mRetrieveBlob; + std::shared_ptr mDebugUpdateStat; + mutable utils::Mutex mMutex; }; } // namespace filament diff --git a/package/ios/libs/filament/include/backend/PresentCallable.h b/package/ios/libs/filament/include/backend/PresentCallable.h index 4402f222..ea3380c6 100644 --- a/package/ios/libs/filament/include/backend/PresentCallable.h +++ b/package/ios/libs/filament/include/backend/PresentCallable.h @@ -27,12 +27,12 @@ namespace filament::backend { * A PresentCallable is a callable object that, when called, schedules a frame for presentation on * a SwapChain. * - * Typically, Filament's backend is responsible scheduling a frame's presentation. However, there - * are certain cases where the application might want to control when a frame is scheduled for + * Typically, Filament's backend is responsible for scheduling a frame's presentation. However, + * there are certain cases where the application might want to control when a frame is scheduled for * presentation. * * For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present - * within a CATransation: + * within a CATransaction: * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * void myFrameScheduledCallback(PresentCallable presentCallable, void* user) { @@ -48,21 +48,20 @@ namespace filament::backend { * and optional user data: * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * swapChain->setFrameScheduledCallback(myFrameScheduledCallback, nullptr); + * swapChain->setFrameScheduledCallback(nullptr, myFrameScheduledCallback); * if (renderer->beginFrame(swapChain)) { * renderer->render(view); * renderer->endFrame(); * } * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * - * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other - * backends ignore the callback (which will never be called) and proceed normally. + * @remark The PresentCallable mechanism for user-controlled presentation is only supported by + * Filament's Metal backend. On other backends, the FrameScheduledCallback is still invoked, but the + * PresentCallable passed to it is a no-op and calling it has no effect. * - * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. - * - * Applications *must* call each PresentCallable they receive. Each PresentCallable represents a - * frame that is waiting to be presented. If an application fails to call a PresentCallable, a - * memory leak could occur. To "cancel" the presentation of a frame, pass false to the + * When using the Metal backend, applications *must* call each PresentCallable they receive. Each + * PresentCallable represents a frame that is waiting to be presented, and failing to call it + * will result in a memory leak. To "cancel" the presentation of a frame, pass false to the * PresentCallable, which will cancel the presentation of the frame and release associated memory. * * @see Renderer, SwapChain::setFrameScheduledCallback @@ -71,6 +70,7 @@ class UTILS_PUBLIC PresentCallable { public: using PresentFn = void(*)(bool presentFrame, void* user); + static void noopPresent(bool, void*) {} PresentCallable(PresentFn fn, void* user) noexcept; ~PresentCallable() noexcept = default; diff --git a/package/ios/libs/filament/include/backend/Program.h b/package/ios/libs/filament/include/backend/Program.h index fe1c4a9b..431e939e 100644 --- a/package/ios/libs/filament/include/backend/Program.h +++ b/package/ios/libs/filament/include/backend/Program.h @@ -20,17 +20,21 @@ #include #include #include -#include #include -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers +#include +#include +#include +#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { class Program { @@ -40,36 +44,40 @@ class Program { static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT; static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT; - struct Sampler { - utils::CString name = {}; // name of the sampler in the shader - uint32_t binding = 0; // binding point of the sampler in the shader + struct Descriptor { + utils::CString name; + DescriptorType type; + descriptor_binding_t binding; }; - struct SamplerGroupData { - utils::FixedCapacityVector samplers; - ShaderStageFlags stageFlags = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS; - }; + using SpecializationConstant = std::variant; + using DescriptorSetLayoutArray = std::array; - struct Uniform { + struct Uniform { // For ES2 support utils::CString name; // full qualified name of the uniform field uint16_t offset; // offset in 'uint32_t' into the uniform buffer uint8_t size; // >1 for arrays UniformType type; // uniform type }; - using UniformBlockInfo = std::array; - using UniformInfo = utils::FixedCapacityVector; - using SamplerGroupInfo = std::array; + using DescriptorBindingsInfo = utils::FixedCapacityVector; + using DescriptorSetInfo = std::array; + using SpecializationConstantsInfo = utils::FixedCapacityVector; using ShaderBlob = utils::FixedCapacityVector; using ShaderSource = std::array; + using AttributesInfo = utils::FixedCapacityVector>; + using UniformInfo = utils::FixedCapacityVector; + using BindingUniformsInfo = utils::FixedCapacityVector< + std::tuple>; + Program() noexcept; Program(const Program& rhs) = delete; Program& operator=(const Program& rhs) = delete; Program(Program&& rhs) noexcept; - Program& operator=(Program&& rhs) noexcept = delete; + Program& operator=(Program&& rhs) noexcept; ~Program() noexcept; @@ -77,91 +85,116 @@ class Program { // sets the material name and variant for diagnostic purposes only Program& diagnostics(utils::CString const& name, - utils::Invocable&& logger); + utils::Invocable&& logger); - // sets one of the program's shader (e.g. vertex, fragment) + // Sets one of the program's shader (e.g. vertex, fragment) // string-based shaders are null terminated, consequently the size parameter must include the // null terminating character. Program& shader(ShaderStage shader, void const* data, size_t size); - // Note: This is only needed for GLES3.0 backends, because the layout(binding=) syntax is - // not permitted in glsl. The backend needs a way to associate a uniform block - // to a binding point. - Program& uniformBlockBindings( - utils::FixedCapacityVector> const& uniformBlockBindings) noexcept; - - // Note: This is only needed for GLES2.0, this is used to emulate UBO. This function tells - // the program everything it needs to know about the uniforms at a given binding - Program& uniforms(uint32_t index, UniformInfo const& uniforms) noexcept; - - // Note: This is only needed for GLES2.0. - Program& attributes( - utils::FixedCapacityVector> attributes) noexcept; - - // sets the 'bindingPoint' sampler group descriptor for this program. - // 'samplers' can be destroyed after this call. - // This effectively associates a set of (BindingPoints, index) to a texture unit in the shader. - // Or more precisely, what layout(binding=) is set to in GLSL. - Program& setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags, - Sampler const* samplers, size_t count) noexcept; - - struct SpecializationConstant { - using Type = std::variant; - uint32_t id; // id set in glsl - Type value; // value and type + // Sets the language of the shader sources provided with shader() (defaults to ESSL3) + Program& shaderLanguage(ShaderLanguage shaderLanguage); + + // Descriptor binding (set, binding, type -> shader name) info + Program& descriptorBindings(backend::descriptor_set_t set, + DescriptorBindingsInfo descriptorBindings) noexcept; + + Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept; + + struct PushConstant { + utils::CString name; + ConstantType type; }; - Program& specializationConstants( - utils::FixedCapacityVector specConstants) noexcept; + Program& pushConstants(ShaderStage stage, + utils::FixedCapacityVector constants) noexcept; Program& cacheId(uint64_t cacheId) noexcept; Program& multiview(bool multiview) noexcept; + // For ES2 support only... + Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms); + Program& attributes(AttributesInfo attributes) noexcept; + + // + // Getters for program construction... + // + ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } ShaderSource& getShadersSource() noexcept { return mShadersSource; } - UniformBlockInfo const& getUniformBlockBindings() const noexcept { return mUniformBlocks; } - UniformBlockInfo& getUniformBlockBindings() noexcept { return mUniformBlocks; } + utils::CString const& getName() const noexcept { return mName; } + utils::CString& getName() noexcept { return mName; } - SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; } - SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; } + auto const& getShaderLanguage() const { return mShaderLanguage; } - auto const& getBindingUniformInfo() const { return mBindingUniformInfo; } - auto& getBindingUniformInfo() { return mBindingUniformInfo; } + uint64_t getCacheId() const noexcept { return mCacheId; } - auto const& getAttributes() const { return mAttributes; } - auto& getAttributes() { return mAttributes; } + bool isMultiview() const noexcept { return mMultiview; } - utils::CString const& getName() const noexcept { return mName; } - utils::CString& getName() noexcept { return mName; } + CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } - utils::FixedCapacityVector const& getSpecializationConstants() const noexcept { + SpecializationConstantsInfo const& getSpecializationConstants() const noexcept { return mSpecializationConstants; } - utils::FixedCapacityVector& getSpecializationConstants() noexcept { + + SpecializationConstantsInfo& getSpecializationConstants() noexcept { return mSpecializationConstants; } - uint64_t getCacheId() const noexcept { return mCacheId; } + DescriptorSetInfo& getDescriptorBindings() noexcept { + return mDescriptorBindings; + } - bool isMultiview() const noexcept { return mMultiview; } + inline Program& descriptorLayout(backend::descriptor_set_t set, + DescriptorSetLayout descriptorLayout) noexcept { + mDescriptorLayouts[set] = std::move(descriptorLayout); + return *this; + } - CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } + const DescriptorSetLayoutArray& getDescriptorSetLayouts() const noexcept { + return mDescriptorLayouts; + } + + utils::FixedCapacityVector const& getPushConstants( + ShaderStage stage) const noexcept { + return mPushConstants[static_cast(stage)]; + } + + utils::FixedCapacityVector& getPushConstants(ShaderStage stage) noexcept { + return mPushConstants[static_cast(stage)]; + } + + auto const& getBindingUniformInfo() const { return mBindingUniformsInfo; } + auto& getBindingUniformInfo() { return mBindingUniformsInfo; } + + auto const& getAttributes() const { return mAttributes; } + auto& getAttributes() { return mAttributes; } private: friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder); - UniformBlockInfo mUniformBlocks = {}; - SamplerGroupInfo mSamplerGroups = {}; ShaderSource mShadersSource; + ShaderLanguage mShaderLanguage = ShaderLanguage::ESSL3; utils::CString mName; uint64_t mCacheId{}; - utils::Invocable mLogger; - utils::FixedCapacityVector mSpecializationConstants; - utils::FixedCapacityVector> mAttributes; - std::array mBindingUniformInfo; CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; + utils::Invocable + mLogger; + SpecializationConstantsInfo mSpecializationConstants; + std::array, SHADER_TYPE_COUNT> mPushConstants; + DescriptorSetInfo mDescriptorBindings; + + // Descriptions for descriptor set layouts that may be used for this Program, which + // can be useful for attempting to compile the pipeline ahead of time. + DescriptorSetLayoutArray mDescriptorLayouts; + + // For ES2 support only + AttributesInfo mAttributes; + BindingUniformsInfo mBindingUniformsInfo; + // Indicates the current engine was initialized with multiview stereo, and the variant for this // program contains STE flag. This will be referred later for the OpenGL shader compiler to // determine whether shader code replacement for the num_views should be performed. diff --git a/package/ios/libs/filament/include/backend/TargetBufferInfo.h b/package/ios/libs/filament/include/backend/TargetBufferInfo.h index ce23fc5f..c2538161 100644 --- a/package/ios/libs/filament/include/backend/TargetBufferInfo.h +++ b/package/ios/libs/filament/include/backend/TargetBufferInfo.h @@ -19,27 +19,47 @@ #include -#include +#include #include #include +namespace utils::io { +class ostream; +} // namespace utils::io + namespace filament::backend { //! \privatesection struct TargetBufferInfo { + // note: the parameters of this constructor are not in the order of this structure's fields + TargetBufferInfo(Handle handle, uint8_t const level, uint16_t const layer) noexcept + : handle(std::move(handle)), level(level), layer(layer) { + } + + TargetBufferInfo(Handle handle, uint8_t const level) noexcept + : handle(handle), level(level) { + } + + TargetBufferInfo(Handle handle) noexcept // NOLINT(*-explicit-constructor) + : handle(handle) { + } + + TargetBufferInfo() noexcept = default; + // texture to be used as render target Handle handle; - // starting layer index for multiview. This value is only used when the `layerCount` for the - // render target is greater than 1. - uint8_t baseViewIndex = 0; - // level to be used uint8_t level = 0; - // for cubemaps and 3D textures. See TextureCubemapFace for the face->layer mapping + // - For cubemap textures, this indicates the face of the cubemap. See TextureCubemapFace for + // the face->layer mapping) + // - For 2d array, cubemap array, and 3d textures, this indicates an index of a single layer of + // them. + // - For multiview textures (i.e., layerCount for the RenderTarget is greater than 1), this + // indicates a starting layer index of the current 2d array texture for multiview. uint16_t layer = 0; }; @@ -54,17 +74,17 @@ class MRT { TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT]; public: - TargetBufferInfo const& operator[](size_t i) const noexcept { + TargetBufferInfo const& operator[](size_t const i) const noexcept { return mInfos[i]; } - TargetBufferInfo& operator[](size_t i) noexcept { + TargetBufferInfo& operator[](size_t const i) noexcept { return mInfos[i]; } MRT() noexcept = default; - MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions) + MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions, *-explicit-constructor) : mInfos{ color } { } @@ -84,7 +104,7 @@ class MRT { // this is here for backward compatibility MRT(Handle handle, uint8_t level, uint16_t layer) noexcept - : mInfos{{ handle, 0, level, layer }} { + : mInfos{{ handle, level, layer }} { } }; diff --git a/package/ios/libs/filament/include/backend/platforms/AndroidNdk.h b/package/ios/libs/filament/include/backend/platforms/AndroidNdk.h new file mode 100644 index 00000000..a3766710 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/AndroidNdk.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2025 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 FILAMENT_BACKEND_ANDROIDNDK_H +#define FILAMENT_BACKEND_ANDROIDNDK_H + +#include +#include + +#define FILAMENT_REQUIRES_API(x) __attribute__((__availability__(android,introduced=x))) + +#define FILAMENT_USE_DLSYM(api) (__ANDROID_API__ < (api)) + +namespace filament::backend { + +class AndroidNdk { +public: + AndroidNdk(); + +#if FILAMENT_USE_DLSYM(26) + + static void AHardwareBuffer_acquire( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_acquire(buffer); + } + + static void AHardwareBuffer_release( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_release(buffer); + } + + static void AHardwareBuffer_describe( + AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) { + ndk.AHardwareBuffer_describe(buffer, desc); + } + +#else + + static void AHardwareBuffer_acquire( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_acquire(buffer); + } + static void AHardwareBuffer_release( + AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_release(buffer); + } + static void AHardwareBuffer_describe( + AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) { + ::AHardwareBuffer_describe(buffer, desc); + } + +#endif + +private: +#if FILAMENT_USE_DLSYM(26) + static struct Ndk { + void (*AHardwareBuffer_acquire)(AHardwareBuffer*); + void (*AHardwareBuffer_release)(AHardwareBuffer*); + void (*AHardwareBuffer_describe)(AHardwareBuffer const*, AHardwareBuffer_Desc*); + } ndk; +#endif +}; + +} // filament::backend + +#endif //FILAMENT_BACKEND_ANDROIDNDK_H diff --git a/package/ios/libs/filament/include/backend/platforms/OpenGLPlatform.h b/package/ios/libs/filament/include/backend/platforms/OpenGLPlatform.h index dec6f47b..d71548e3 100644 --- a/package/ios/libs/filament/include/backend/platforms/OpenGLPlatform.h +++ b/package/ios/libs/filament/include/backend/platforms/OpenGLPlatform.h @@ -23,9 +23,11 @@ #include #include +#include #include #include +#include namespace filament::backend { @@ -51,12 +53,23 @@ class OpenGLPlatform : public Platform { ~OpenGLPlatform() noexcept override; public: - struct ExternalTexture { - unsigned int target; // GLenum target - unsigned int id; // GLuint id + unsigned int target; // GLenum target + unsigned int id; // GLuint id }; + /** + * Return the OpenGL vendor string of the specified Driver instance. + * @return The GL_VENDOR string + */ + static utils::CString getVendorString(Driver const* UTILS_NONNULL driver); + + /** + * Return the OpenGL vendor string of the specified Driver instance + * @return The GL_RENDERER string + */ + static utils::CString getRendererString(Driver const* UTILS_NONNULL driver); + /** * Called by the driver to destroy the OpenGL context. This should clean up any windows * or buffers from initialization. This is for instance where `eglDestroyContext` would be @@ -72,6 +85,15 @@ class OpenGLPlatform : public Platform { */ virtual bool isSRGBSwapChainSupported() const noexcept; + /** + * Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES flag. + * The default implementation returns false. + * + * @param samples The number of samples + * @return true if SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES is supported, false otherwise. + */ + virtual bool isMSAASwapChainSupported(uint32_t samples) const noexcept; + /** * Return whether protected contexts are supported by this backend. * If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be @@ -90,7 +112,7 @@ class OpenGLPlatform : public Platform { * */ virtual SwapChain* UTILS_NULLABLE createSwapChain( - void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0; + void* UTILS_NULLABLE nativeWindow, uint64_t flags) = 0; /** * Called by the driver create a headless SwapChain. @@ -104,7 +126,7 @@ class OpenGLPlatform : public Platform { * A void* might be enough. */ virtual SwapChain* UTILS_NULLABLE createSwapChain( - uint32_t width, uint32_t height, uint64_t flags) noexcept = 0; + uint32_t width, uint32_t height, uint64_t flags) = 0; /** * Called by the driver to destroys the SwapChain @@ -140,6 +162,23 @@ class OpenGLPlatform : public Platform { */ virtual uint32_t getDefaultFramebufferObject() noexcept; + /** + * Called by the backend when a frame starts. + * @param steady_clock_ns vsync time point on the monotonic clock + * @param refreshIntervalNs refresh interval in nanosecond + * @param frameId a frame id + */ + virtual void beginFrame( + int64_t monotonic_clock_ns, + int64_t refreshIntervalNs, + uint32_t frameId) noexcept; + + /** + * Called by the backend when a frame ends. + * @param frameId the frame id used in beginFrame + */ + virtual void endFrame( + uint32_t frameId) noexcept; /** * Type of contexts available @@ -169,7 +208,7 @@ class OpenGLPlatform : public Platform { */ virtual bool makeCurrent(ContextType type, SwapChain* UTILS_NONNULL drawSwapChain, - SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0; + SwapChain* UTILS_NONNULL readSwapChain) = 0; /** * Called by the driver to make the OpenGL context active on the calling thread and bind @@ -189,7 +228,13 @@ class OpenGLPlatform : public Platform { SwapChain* UTILS_NONNULL drawSwapChain, SwapChain* UTILS_NONNULL readSwapChain, utils::Invocable preContextChange, - utils::Invocable postContextChange) noexcept; + utils::Invocable postContextChange); + + /** + * Called by the backend just before calling commit() + * @see commit() + */ + virtual void preCommit() noexcept; /** * Called by the driver once the current frame finishes drawing. Typically, this should present @@ -240,6 +285,26 @@ class OpenGLPlatform : public Platform { */ virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept; + // -------------------------------------------------------------------------------------------- + // Sync support + + /** + * Creates a Sync. These can be used for frame synchronization externally + * (certain platform implementations can be exported to handles that can + * be used in other processes). + * + * @return A Sync object. + */ + virtual Platform::Sync* UTILS_NONNULL createSync() noexcept; + + /** + * Destroys a sync. If called with a sync not created by this platform + * object, this will lead to undefined behavior. + * + * @param sync The sync to destroy, that was created by this platform + * instance. + */ + virtual void destroySync(Platform::Sync* UTILS_NONNULL sync) noexcept; // -------------------------------------------------------------------------------------------- // Streaming support @@ -284,6 +349,13 @@ class OpenGLPlatform : public Platform { virtual void updateTexImage(Stream* UTILS_NONNULL stream, int64_t* UTILS_NONNULL timestamp) noexcept; + /** + * Returns the transform matrix of the texture attached to the stream. + * @param stream Stream to get the transform matrix from + * @param uvTransform Output parameter: Transform matrix of the image bound to the texture. Returns identity if not supported. + */ + virtual math::mat3f getTransformMatrix(Stream* UTILS_NONNULL stream) noexcept; + // -------------------------------------------------------------------------------------------- // External Image support @@ -301,36 +373,45 @@ class OpenGLPlatform : public Platform { * Destroys an external texture handle and associated data. * @param texture a pointer to the handle to destroy. */ - virtual void destroyExternalImage(ExternalTexture* UTILS_NONNULL texture) noexcept; + virtual void destroyExternalImageTexture(ExternalTexture* UTILS_NONNULL texture) noexcept; // called on the application thread to allow Filament to take ownership of the image /** * Takes ownership of the externalImage. The externalImage parameter depends on the Platform's - * concrete implementation. Ownership is released when destroyExternalImage() is called. + * concrete implementation. Ownership is released when destroyExternalImageTexture() is called. * * WARNING: This is called synchronously from the application thread (NOT the Driver thread) * * @param externalImage A token representing the platform's external image. * @see destroyExternalImage + * @{ */ virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept; + virtual void retainExternalImage(ExternalImageHandleRef externalImage) noexcept; + /** @}*/ + /** * Called to bind the platform-specific externalImage to an ExternalTexture. * ExternalTexture::id is guaranteed to be bound when this method is called and ExternalTexture * is updated with new values for id/target if necessary. * * WARNING: this method is not allowed to change the bound texture, or must restore the previous - * binding upon return. This is to avoid problem with a backend doing state caching. + * binding upon return. This is to avoid a problem with a backend doing state caching. * * @param externalImage The platform-specific external image. * @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary. * @return true on success, false on error. + * @{ */ virtual bool setExternalImage(void* UTILS_NONNULL externalImage, ExternalTexture* UTILS_NONNULL texture) noexcept; + virtual bool setExternalImage(ExternalImageHandleRef externalImage, + ExternalTexture* UTILS_NONNULL texture) noexcept; + /** @}*/ + /** * The method allows platforms to convert a user-supplied external image object into a new type * (e.g. HardwareBuffer => EGLImage). The default implementation returns source. @@ -349,7 +430,7 @@ class OpenGLPlatform : public Platform { virtual bool isExtraContextSupported() const noexcept; /** - * Creates an OpenGL context with the same configuration than the main context and makes it + * Creates an OpenGL context with the same configuration as the main context and makes it * current to the current thread. Must not be called from the main driver thread. * createContext() is only supported if isExtraContextSupported() returns true. * These additional contexts will be automatically terminated in terminate. @@ -362,7 +443,7 @@ class OpenGLPlatform : public Platform { /** * Detach and destroy the current context if any and releases all resources associated to - * this thread. + * this thread. This must be called from the same thread where createContext() was called. */ virtual void releaseContext() noexcept; }; diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformCocoaGL.h b/package/ios/libs/filament/include/backend/platforms/PlatformCocoaGL.h index 97c9c3ce..c424ec4f 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformCocoaGL.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformCocoaGL.h @@ -17,7 +17,6 @@ #ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H #define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H -#include #include #include @@ -34,12 +33,14 @@ class PlatformCocoaGL : public OpenGLPlatform { PlatformCocoaGL(); ~PlatformCocoaGL() noexcept override; + ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept; + protected: // -------------------------------------------------------------------------------------------- // Platform Interface Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; // Currently returns 0 int getOSVersion() const noexcept override; @@ -57,12 +58,14 @@ class PlatformCocoaGL : public OpenGLPlatform { SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; - OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; - void destroyExternalImage(ExternalTexture* texture) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; void retainExternalImage(void* externalImage) noexcept override; bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override; + void retainExternalImage(ExternalImageHandleRef externalImage) noexcept override; + bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override; private: PlatformCocoaGLImpl* pImpl = nullptr; diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h b/package/ios/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h index e7f1d1ff..94ca86d1 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformCocoaTouchGL.h @@ -32,11 +32,13 @@ class PlatformCocoaTouchGL : public OpenGLPlatform { PlatformCocoaTouchGL(); ~PlatformCocoaTouchGL() noexcept override; + ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept; + // -------------------------------------------------------------------------------------------- // Platform Interface Driver* createDriver(void* sharedGLContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; int getOSVersion() const noexcept final { return 0; } @@ -53,13 +55,15 @@ class PlatformCocoaTouchGL : public OpenGLPlatform { SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; - OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; - void destroyExternalImage(ExternalTexture* texture) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; void retainExternalImage(void* externalImage) noexcept override; bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override; + void retainExternalImage(ExternalImageHandleRef externalImage) noexcept override; + bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override; private: PlatformCocoaTouchGLImpl* pImpl = nullptr; diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformEGL.h b/package/ios/libs/filament/include/backend/platforms/PlatformEGL.h index ef687653..8b8f2a95 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformEGL.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformEGL.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -47,6 +48,11 @@ class PlatformEGL : public OpenGLPlatform { // Return true if we're on an OpenGL platform (as opposed to OpenGL ES). false by default. virtual bool isOpenGL() const noexcept; + /** + * Creates an ExternalImage from a EGLImageKHR + */ + ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept; + protected: // -------------------------------------------------------------------------------------------- // Helper for EGL configs and attributes parameters @@ -75,8 +81,7 @@ class PlatformEGL : public OpenGLPlatform { * Initializes EGL, creates the OpenGL context and returns a concrete Driver implementation * that supports OpenGL/OpenGL ES. */ - Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) override; /** * This returns zero. This method can be overridden to return something more useful. @@ -94,22 +99,19 @@ class PlatformEGL : public OpenGLPlatform { void terminate() noexcept override; bool isProtectedContextSupported() const noexcept override; - bool isSRGBSwapChainSupported() const noexcept override; - SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; - SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; - void destroySwapChain(SwapChain* swapChain) noexcept override; + bool isMSAASwapChainSupported(uint32_t samples) const noexcept override; bool isSwapChainProtected(SwapChain* swapChain) noexcept override; ContextType getCurrentContextType() const noexcept override; bool makeCurrent(ContextType type, SwapChain* drawSwapChain, - SwapChain* readSwapChain) noexcept override; + SwapChain* readSwapChain) override; void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain, utils::Invocable preContextChange, - utils::Invocable postContextChange) noexcept override; + utils::Invocable postContextChange) override; void commit(SwapChain* swapChain) noexcept override; @@ -118,12 +120,13 @@ class PlatformEGL : public OpenGLPlatform { void destroyFence(Fence* fence) noexcept override; FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept override; - OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; - void destroyExternalImage(ExternalTexture* texture) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override; + bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override; /** - * Logs glGetError() to slog.e + * Logs glGetError() to LOG(ERROR) * @param name a string giving some context on the error. Typically __func__. */ static void logEglError(const char* name) noexcept; @@ -143,25 +146,18 @@ class PlatformEGL : public OpenGLPlatform { EGLContext getContextForType(ContextType type) const noexcept; // makes the draw and read surface current without changing the current context - EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept { + EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) { return egl.makeCurrent(drawSurface, readSurface); } // makes context current and set draw and read surfaces to EGL_NO_SURFACE - EGLBoolean makeCurrent(EGLContext context) noexcept { + EGLBoolean makeCurrent(EGLContext context) { return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface); } - // TODO: this should probably use getters instead. - EGLDisplay mEGLDisplay = EGL_NO_DISPLAY; - EGLContext mEGLContext = EGL_NO_CONTEXT; - EGLContext mEGLContextProtected = EGL_NO_CONTEXT; - EGLSurface mEGLDummySurface = EGL_NO_SURFACE; - ContextType mCurrentContextType = ContextType::NONE; - // mEGLConfig is valid only if ext.egl.KHR_no_config_context is false - EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR; - Config mContextAttribs; - std::vector mAdditionalContexts; + EGLDisplay getEglDisplay() const noexcept { return mEGLDisplay; } + EGLConfig getEglConfig() const noexcept { return mEGLConfig; } + EGLConfig getSuitableConfigForSwapChain(uint64_t flags, bool window, bool pbuffer) const; // supported extensions detected at runtime struct { @@ -178,7 +174,11 @@ class PlatformEGL : public OpenGLPlatform { } egl; } ext; - struct SwapChainEGL : public Platform::SwapChain { + struct SwapChainEGL : public SwapChain { + SwapChainEGL(PlatformEGL const& platform, void* nativeWindow, uint64_t flags); + SwapChainEGL(PlatformEGL const& platform, uint32_t width, uint32_t height, uint64_t flags); + void terminate(PlatformEGL& platform); + EGLSurface sur = EGL_NO_SURFACE; Config attribs{}; EGLNativeWindowType nativeWindow{}; @@ -188,10 +188,31 @@ class PlatformEGL : public OpenGLPlatform { void initializeGlExtensions() noexcept; -protected: - EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const; + struct ExternalImageEGL : public ExternalImage { + EGLImageKHR eglImage = EGL_NO_IMAGE; + protected: + ~ExternalImageEGL() override; + }; private: + // prevent derived classes' implementations to call through + [[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override; + [[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override; + void destroySwapChain(SwapChain* swapChain) noexcept override; + + EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const; + + EGLDisplay mEGLDisplay = EGL_NO_DISPLAY; + EGLContext mEGLContext = EGL_NO_CONTEXT; + EGLContext mEGLContextProtected = EGL_NO_CONTEXT; + EGLSurface mEGLDummySurface = EGL_NO_SURFACE; + ContextType mCurrentContextType = ContextType::NONE; + // mEGLConfig is valid only if ext.egl.KHR_no_config_context is false + EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR; + Config mContextAttribs; + std::vector mAdditionalContexts; + bool mMSAA4XSupport = false; + class EGL { EGLDisplay& mEGLDisplay; EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE; @@ -200,12 +221,14 @@ class PlatformEGL : public OpenGLPlatform { public: explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {} EGLBoolean makeCurrent(EGLContext context, - EGLSurface drawSurface, EGLSurface readSurface) noexcept; + EGLSurface drawSurface, EGLSurface readSurface); - EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept { + EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) { return makeCurrent(mCurrentContext, drawSurface, readSurface); } } egl{ mEGLDisplay }; + + bool checkIfMSAASwapChainSupported(uint32_t samples) const noexcept; }; } // namespace filament::backend diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformEGLAndroid.h b/package/ios/libs/filament/include/backend/platforms/PlatformEGLAndroid.h index 32f83038..9410a681 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformEGLAndroid.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformEGLAndroid.h @@ -17,11 +17,21 @@ #ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H #define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H +#include "AndroidNdk.h" + #include +#include #include #include #include +#include +#include + +#include + +#include + #include #include @@ -33,13 +43,45 @@ class ExternalStreamManagerAndroid; * A concrete implementation of OpenGLPlatform and subclass of PlatformEGL that supports * EGL on Android. It adds Android streaming functionality to PlatformEGL. */ -class PlatformEGLAndroid : public PlatformEGL { +class PlatformEGLAndroid : public PlatformEGL, public AndroidNdk { public: PlatformEGLAndroid() noexcept; ~PlatformEGLAndroid() noexcept override; + /** + * Creates an ExternalImage from a EGLImageKHR + */ + ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept; + + struct UTILS_PUBLIC ExternalImageDescAndroid { + uint32_t width; // Texture width + uint32_t height; // Texture height + TextureFormat format;// Texture format + TextureUsage usage; // Texture usage flags + }; + + ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept; + + /** + * Converts a sync to an external file descriptor, if possible. Accepts an + * opaque handle to a sync, as well as a pointer to where the fd should be + * stored. + * @param sync The sync to be converted to a file descriptor. + * @param fd A pointer to where the file descriptor should be stored. + * @return `true` on success, `false` on failure. The default implementation + * returns `false`. + */ + bool convertSyncToFd(Sync* sync, int* fd) noexcept; + protected: + struct { + struct { + bool ANDROID_presentation_time = false; + bool ANDROID_get_frame_timestamps = false; + bool ANDROID_native_fence_sync = false; + } egl; + } ext; // -------------------------------------------------------------------------------------------- // Platform Interface @@ -51,25 +93,48 @@ class PlatformEGLAndroid : public PlatformEGL { int getOSVersion() const noexcept override; Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; + + bool isCompositorTimingSupported() const noexcept override; + + bool queryCompositorTiming(SwapChain const* swapchain, + CompositorTiming* outCompositorTiming) const noexcept override; + + bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override; + + bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId, + FrameTimestamps* outFrameTimestamps) const noexcept override; // -------------------------------------------------------------------------------------------- // OpenGLPlatform Interface + struct SyncEGLAndroid : public Sync { + EGLSyncKHR sync; + }; + void terminate() noexcept override; + void beginFrame( + int64_t monotonic_clock_ns, + int64_t refreshIntervalNs, + uint32_t frameId) noexcept override; + + void preCommit() noexcept override; + /** * Set the presentation time using `eglPresentationTimeANDROID` * @param presentationTimeInNanosecond */ void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept override; - Stream* createStream(void* nativeStream) noexcept override; void destroyStream(Stream* stream) noexcept override; + Sync* createSync() noexcept override; + void destroySync(Sync* sync) noexcept override; void attach(Stream* stream, intptr_t tname) noexcept override; void detach(Stream* stream) noexcept override; void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override; + math::mat3f getTransformMatrix(Stream* stream) noexcept override; /** * Converts a AHardwareBuffer to EGLImage @@ -78,9 +143,61 @@ class PlatformEGLAndroid : public PlatformEGL { */ AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override; + ExternalTexture* createExternalImageTexture() noexcept override; + void destroyExternalImageTexture(ExternalTexture* texture) noexcept override; + + struct ExternalImageEGLAndroid : public ExternalImageEGL { + AHardwareBuffer* aHardwareBuffer = nullptr; + uint32_t width; // Texture width + uint32_t height; // Texture height + TextureFormat format;// Texture format + TextureUsage usage; // Texture usage flags + bool sRGB = false; + + protected: + ~ExternalImageEGLAndroid() override; + }; + + bool setExternalImage(ExternalImageHandleRef externalImage, + ExternalTexture* texture) noexcept override; + bool setImage(ExternalImageEGLAndroid const* eglExternalImage, + ExternalTexture* texture) noexcept; + + bool makeCurrent(ContextType type, + SwapChain* drawSwapChain, + SwapChain* readSwapChain) override; + private: + struct SwapChainEGLAndroid; + struct AndroidDetails; + + // prevent derived classes' implementations to call through + [[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override; + [[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override; + void destroySwapChain(SwapChain* swapChain) noexcept override; + + bool isProducerThrottlingControlSupported() const; + + int32_t setProducerThrottlingEnabled(EGLNativeWindowType nativeWindow, bool enabled) const; + + struct InitializeJvmForPerformanceManagerIfNeeded { + InitializeJvmForPerformanceManagerIfNeeded(); + }; + + struct ExternalTextureAndroid : public ExternalTexture { + EGLImageKHR eglImage = EGL_NO_IMAGE; + }; + int mOSVersion; ExternalStreamManagerAndroid& mExternalStreamManager; + AndroidDetails& mAndroidDetails; + InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded; + utils::PerformanceHintManager mPerformanceHintManager; + utils::PerformanceHintManager::Session mPerformanceHintSession; + using clock = std::chrono::high_resolution_clock; + clock::time_point mStartTimeOfActualWork; + SwapChainEGLAndroid* mCurrentDrawSwapChain{}; + bool mAssertNativeWindowIsValid = false; }; } // namespace filament::backend diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformEGLHeadless.h b/package/ios/libs/filament/include/backend/platforms/PlatformEGLHeadless.h index 40d285b9..300748e0 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformEGLHeadless.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformEGLHeadless.h @@ -29,7 +29,7 @@ class PlatformEGLHeadless : public PlatformEGL { PlatformEGLHeadless() noexcept; Driver* createDriver(void* sharedContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const Platform::DriverConfig& driverConfig) override; protected: bool isOpenGL() const noexcept override; diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformGLX.h b/package/ios/libs/filament/include/backend/platforms/PlatformGLX.h index 796e27a1..8d0eda33 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformGLX.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformGLX.h @@ -26,7 +26,10 @@ #include +#include +#include #include +#include namespace filament::backend { @@ -39,7 +42,7 @@ class PlatformGLX : public OpenGLPlatform { // Platform Interface Driver* createDriver(void* sharedGLContext, - const DriverConfig& driverConfig) noexcept override; + const DriverConfig& driverConfig) override; int getOSVersion() const noexcept final override { return 0; } @@ -48,18 +51,26 @@ class PlatformGLX : public OpenGLPlatform { void terminate() noexcept override; + bool isExtraContextSupported() const noexcept override; + void createContext(bool shared) override; + void releaseContext() noexcept override; + SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; private: - Display *mGLXDisplay; - GLXContext mGLXContext; - GLXFBConfig* mGLXConfig; + Display* mGLXDisplay; + GLXContext mGLXContext{}; + GLXFBConfig mGLXConfig{}; GLXPbuffer mDummySurface; std::vector mPBuffers; + + // Variables for shared contexts + std::unordered_map mAdditionalContexts; + std::shared_mutex mAdditionalContextsLock; }; } // namespace filament::backend diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformMetal-ObjC.h b/package/ios/libs/filament/include/backend/platforms/PlatformMetal-ObjC.h new file mode 100644 index 00000000..2635ea67 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/PlatformMetal-ObjC.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 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_PRIVATE_PLATFORMMETAL_OBJC_H +#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H + +#import + +namespace filament::backend { + +struct MetalDevice { + id device; +}; + +struct MetalCommandQueue { + id commandQueue; +}; + +struct MetalCommandBuffer { + id commandBuffer; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformMetal.h b/package/ios/libs/filament/include/backend/platforms/PlatformMetal.h new file mode 100644 index 00000000..a2e3ac25 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/PlatformMetal.h @@ -0,0 +1,118 @@ +/* + * 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_PRIVATE_PLATFORMMETAL_H +#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H + +#include +#include + +namespace filament::backend { + +struct PlatformMetalImpl; + +// In order for this header to be compatible with Objective-C and C++, we use these wrappers around +// id objects. +// See PlatformMetal-Objc.h. +struct MetalDevice; +struct MetalCommandQueue; +struct MetalCommandBuffer; + +class PlatformMetal final : public Platform { +public: + PlatformMetal(); + ~PlatformMetal() noexcept override; + + Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override; + int getOSVersion() const noexcept override { return 0; } + + /** + * Optionally initializes the Metal platform by acquiring resources necessary for rendering. + * + * This method attempts to acquire a Metal device and command queue, returning true if both are + * successfully obtained, or false otherwise. Typically, these objects are acquired when + * the Metal backend is initialized. This method allows clients to check for their availability + * earlier. + * + * Calling initialize() is optional and safe to do so multiple times. After initialize() returns + * true, subsequent calls will continue to return true but have no effect. + * + * initialize() must be called from the main thread. + * + * @returns true if the device and command queue have been successfully obtained; false + * otherwise. + */ + bool initialize() noexcept; + + /** + * Obtain the preferred Metal device object for the backend to use. + * + * On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is + * free to decide which one to use. On mobile systems with a single GPU, implementations should + * simply return the result of MTLCreateSystemDefaultDevice(); + * + * createDevice is called by the Metal backend from the backend thread. + */ + virtual void createDevice(MetalDevice& outDevice) noexcept; + + /** + * Create a command submission queue on the Metal device object. + * + * createCommandQueue is called by the Metal backend from the backend thread. + * + * @param device The device which was returned from createDevice() + */ + virtual void createCommandQueue( + MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept; + + /** + * Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is + * guaranteed to execute before all subsequent command buffers created either by Filament, or + * further calls to this method. + * + * createAndEnqueueCommandBuffer must be called from the main thread. + */ + void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept; + + /** + * The action to take if a Drawable cannot be acquired. + * + * Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the + * completion of each frame. These are limited and provided round-robin style by the system. + * + * setDrawableFailureBehavior must be called from the main thread. + */ + enum class DrawableFailureBehavior : uint8_t { + /** + * Terminates the application and reports an error message (default). + */ + PANIC, + /* + * Aborts execution of the current frame. The Metal backend will attempt to acquire a new + * drawable at the next frame. + */ + ABORT_FRAME + }; + void setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept; + DrawableFailureBehavior getDrawableFailureBehavior() const noexcept; + +private: + PlatformMetalImpl* pImpl = nullptr; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformOSMesa.h b/package/ios/libs/filament/include/backend/platforms/PlatformOSMesa.h new file mode 100644 index 00000000..10e1fb18 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/PlatformOSMesa.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 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_OPENGL_OPENGL_PLATFORM_OSMESA_H +#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H + +#include + +#include "bluegl/BlueGL.h" + +#if defined(__linux__) +#include +#elif defined(__APPLE__) +#undef GLAPI +#include +#endif + +#include +#include + +namespace filament::backend { + +/** + * A concrete implementation of OpenGLPlatform that uses OSMesa, which is an offscreen + * context that can be used in conjunction with Mesa for software rasterization. + * See https://docs.mesa3d.org/osmesa.html for more information. + */ +class PlatformOSMesa : public OpenGLPlatform { +protected: + // -------------------------------------------------------------------------------------------- + // Platform Interface + + Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override; + + int getOSVersion() const noexcept final override { return 0; } + + // -------------------------------------------------------------------------------------------- + // OpenGLPlatform Interface + + void terminate() noexcept override; + + SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; + SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; + void destroySwapChain(SwapChain* swapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, + SwapChain* readSwapChain) override; + void commit(SwapChain* swapChain) noexcept override; + +private: + OSMesaContext mContext; + void* mOsMesaApi = nullptr; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformWGL.h b/package/ios/libs/filament/include/backend/platforms/PlatformWGL.h index e0003156..49c79812 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformWGL.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformWGL.h @@ -38,7 +38,7 @@ class PlatformWGL : public OpenGLPlatform { // Platform Interface Driver* createDriver(void* sharedGLContext, - const Platform::DriverConfig& driverConfig) noexcept override; + const Platform::DriverConfig& driverConfig) override; int getOSVersion() const noexcept final override { return 0; } @@ -53,7 +53,7 @@ class PlatformWGL : public OpenGLPlatform { SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; protected: @@ -61,8 +61,12 @@ class PlatformWGL : public OpenGLPlatform { HWND mHWnd = NULL; HDC mWhdc = NULL; PIXELFORMATDESCRIPTOR mPfd = {}; - std::vector mAdditionalContexts; std::vector mAttribs; + + // For shared contexts + static constexpr int SHARED_CONTEXT_NUM = 2; + std::vector mAdditionalContexts; + std::atomic mNextFreeSharedContextIndex{0}; }; } // namespace filament::backend diff --git a/package/ios/libs/filament/include/backend/platforms/PlatformWebGL.h b/package/ios/libs/filament/include/backend/platforms/PlatformWebGL.h index 0d83fbb9..21083f58 100644 --- a/package/ios/libs/filament/include/backend/platforms/PlatformWebGL.h +++ b/package/ios/libs/filament/include/backend/platforms/PlatformWebGL.h @@ -33,8 +33,7 @@ class PlatformWebGL : public OpenGLPlatform { // -------------------------------------------------------------------------------------------- // Platform Interface - Driver* createDriver(void* sharedGLContext, - const Platform::DriverConfig& driverConfig) noexcept override; + Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override; int getOSVersion() const noexcept override; @@ -43,10 +42,10 @@ class PlatformWebGL : public OpenGLPlatform { void terminate() noexcept override; - SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override; + SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept override; SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override; void destroySwapChain(SwapChain* swapChain) noexcept override; - bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override; + bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override; void commit(SwapChain* swapChain) noexcept override; }; diff --git a/package/ios/libs/filament/include/backend/platforms/VulkanPlatform.h b/package/ios/libs/filament/include/backend/platforms/VulkanPlatform.h index 6201d644..edfdea29 100644 --- a/package/ios/libs/filament/include/backend/platforms/VulkanPlatform.h +++ b/package/ios/libs/filament/include/backend/platforms/VulkanPlatform.h @@ -17,15 +17,20 @@ #ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H #define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H +#include +#include #include #include #include #include +#include #include -#include +#include +#include +#include #include #include @@ -41,12 +46,24 @@ using SwapChain = Platform::SwapChain; */ struct VulkanPlatformPrivate; +// Forward declare the fence status that will be maintained by the command +// buffer manager. +struct VulkanCmdFence; + /** * A Platform interface that creates a Vulkan backend. */ class VulkanPlatform : public Platform, utils::PrivateImplementation { public: + struct ExtensionHashFn { + std::size_t operator()(utils::CString const& s) const noexcept { + return std::hash{}(s.data()); + } + }; + // Utility for managing device or instance extensions during initialization. + using ExtensionSet = std::unordered_set; + /** * A collection of handles to objects and metadata that comprises a Vulkan context. The client * can instantiate this struct and pass to Engine::Builder::sharedContext if they wishes to @@ -63,6 +80,9 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation fenceStatus) noexcept; + + /** + * Destroys a sync. If called with a sync not created by this platform + * object, this will lead to undefined behavior. + * + * @param sync The sync to destroy, which was created by this platform + * instance. + */ + virtual void destroySync(Platform::Sync* sync) noexcept; + + /** + * Allows implementers to provide instance extensions that they'd like to include in the + * instance creation. + * @return A set of extensions to enable for the instance. + */ + virtual ExtensionSet getRequiredInstanceExtensions() { return {}; } + /** * Destroy the swapchain. * @param handle The handle returned by createSwapChain() @@ -235,14 +303,226 @@ class VulkanPlatform : public Platform, utils::PrivateImplementation; - static ExtensionSet getRequiredInstanceExtensions(); + /** + * @return The family index of the protected graphics queue selected for the + * Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept; + + /** + * @return The index of the protected graphics queue (if there are multiple + * graphics queues) selected for the Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueIndex() const noexcept; + + /** + * @return The protected queue that was selected for the Vulkan backend. + */ + VkQueue getProtectedGraphicsQueue() const noexcept; + + struct ExternalImageMetadata { + /** + * The Filament texture format. + */ + TextureFormat filamentFormat; + + /** + * The Filament texture usage. + */ + TextureUsage filamentUsage; + + /** + * The width of the external image + */ + uint32_t width; + + /** + * The height of the external image + */ + uint32_t height; + + /** + * The layer count of the external image + */ + uint32_t layers; + + /** + * The numbers of samples per texel + */ + VkSampleCountFlagBits samples; + + /** + * The format of the external image + */ + VkFormat format; + + /** + * The type of external format (opaque int) if used. + */ + uint64_t externalFormat; + + /** + * Image usage + */ + VkImageUsageFlags usage; + + /** + * Allocation size + */ + VkDeviceSize allocationSize; + + /** + * Heap information + */ + uint32_t memoryTypeBits; + + /** + * Ycbcr conversion components + */ + VkComponentMapping ycbcrConversionComponents; + + /** + * Ycbcr model + */ + VkSamplerYcbcrModelConversion ycbcrModel; + + /** + * Ycbcr range + */ + VkSamplerYcbcrRange ycbcrRange; + + /** + * Ycbcr x chroma offset + */ + VkChromaLocation xChromaOffset; + + /** + * Ycbcr y chroma offset + */ + VkChromaLocation yChromaOffset; + }; + + + // Note that the image metadata might change per-frame, hence we need a method for extracting + // it. + virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const { + return {}; + } + + struct ImageData { + struct Bundle { + VkImage image = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + + inline bool valid() const noexcept { + return image != VK_NULL_HANDLE; + } + }; + // It's possible for the external image to also have a known VK format. We need to create an + // image for that in case we are not looking to use an external "sampler" with this image. + Bundle internal; + + // If we get a externalFormat in the metadata, then we should create an image with + // VK_FORMAT_UNDEFINED + Bundle external; + }; + + virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const { + return {}; + } + +protected: + struct VulkanSync : public Platform::Sync { + VkFence fence; + std::shared_ptr fenceStatus; + }; + + /** + * Creates the VkInstance used by Filament's Vulkan backend. + * + * This method can be overridden in subclasses to customize VkInstance creation, such as + * adding application-specific layers or extensions. + * + * The provided `createInfo` contains layers and extensions required by Filament. + * If you override this method and need to modify the `createInfo` struct, you must first + * make a copy of it and modify the copy. + * + * @param createInfo The VkInstanceCreateInfo prepared by Filament. + * @return The created VkInstance, or VK_NULL_HANDLE on failure. + */ + virtual VkInstance createVkInstance(VkInstanceCreateInfo const& createInfo) noexcept; + + /** + * Selects a VkPhysicalDevice (GPU) for Filament's Vulkan backend to use. + * + * This method can be overridden in subclasses to implement custom GPU selection logic. + * For example, an application might override this to prefer a discrete GPU over an + * integrated one based on device properties. + * + * The default implementation selects the first device that meets Filament's requirements. + * + * @param instance The VkInstance to enumerate devices from. + * @return The selected VkPhysicalDevice, or VK_NULL_HANDLE if no suitable device is found. + */ + virtual VkPhysicalDevice selectVkPhysicalDevice(VkInstance instance) noexcept; + + /** + * Creates the VkDevice used by Filament's Vulkan backend. + * + * This method can be overridden in subclasses to customize VkDevice creation, such as + * adding application-specific extensions or enabling features. + * + * The provided `createInfo` contains extensions and features required by Filament. + * If you override this method and need to modify the `createInfo` struct, you must first + * make a copy of it and modify the copy. + * + * @param createInfo The VkDeviceCreateInfo prepared by Filament. + * @return The created VkDevice, or VK_NULL_HANDLE on failure. + */ + virtual VkDevice createVkDevice(VkDeviceCreateInfo const& createInfo) noexcept; using SurfaceBundle = std::tuple; - static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, - uint64_t flags) noexcept; + virtual ExtensionSet getSwapchainInstanceExtensions() const = 0; + virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept = 0; + + virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept; + + /** + * Query if transient attachments are supported by the backend. + */ + bool isTransientAttachmentSupported() const noexcept; + +private: + /** + * Contains information about features that should be requested + * when calling vkCreateDevice, based on feature support from + * vkGetPhysicalDeviceFeatures2. + */ + struct MiscDeviceFeatures { + /** + * This allows creation of a VkGraphicsPipeline without a + * render pass specified. + */ + bool dynamicRendering; + + /** + * Allows creation of a 2d image view, or 2d image view array, + * to be created from a 3d VkImage. + */ + bool imageView2Don3DImage; + }; + + void createInstance(ExtensionSet const& requiredExts) noexcept; + + void queryAndSetDeviceFeatures(Platform::DriverConfig const& driverConfig, + ExtensionSet const& instExts, ExtensionSet const& deviceExts, + void* sharedContext) noexcept; + + void createLogicalDeviceAndQueues(ExtensionSet const& deviceExtensions, + VkPhysicalDeviceFeatures const& features, + VkPhysicalDeviceVulkan11Features const& vk11Features, bool createProtectedQueue, + MiscDeviceFeatures const& requestedFeatures) noexcept; friend struct VulkanPlatformPrivate; }; diff --git a/package/ios/libs/filament/include/backend/platforms/VulkanPlatformAndroid.h b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformAndroid.h new file mode 100644 index 00000000..ba82db81 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformAndroid.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_VULKAN_PLATFORM_ANDROID_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H + +#include "AndroidNdk.h" + +#include +#include + +#include + +#include + +namespace filament::backend { + +class VulkanPlatformAndroid : public VulkanPlatform, public AndroidNdk { +public: + ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, + bool sRGB) noexcept; + + struct UTILS_PUBLIC ExternalImageDescAndroid { + uint32_t width; // Texture width + uint32_t height; // Texture height + TextureFormat format;// Texture format + TextureUsage usage; // Texture usage flags + }; + + VulkanPlatformAndroid(); + + ~VulkanPlatformAndroid() noexcept override; + + ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc( + ExternalImageHandleRef externalImage) const noexcept; + + ExternalImageMetadata extractExternalImageMetadata( + ExternalImageHandleRef image) const override; + + ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override; + + /** + * Converts a sync to an external file descriptor, if possible. Accepts an + * opaque handle to a sync, as well as a pointer to where the fd should be + * stored. + * @param sync The sync to be converted to a file descriptor. + * @param fd A pointer to where the file descriptor should be stored. + * @return `true` on success, `false` on failure. The default implementation + * returns `false`. + */ + bool convertSyncToFd(Sync* sync, int* fd) const noexcept; + + int getOSVersion() const noexcept override; + + void terminate() override; + + Driver* createDriver(void* sharedContext, + DriverConfig const& driverConfig) override; + + + bool isCompositorTimingSupported() const noexcept override; + + bool queryCompositorTiming(SwapChain const* swapchain, + CompositorTiming* outCompositorTiming) const noexcept override; + + bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override; + + bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId, + FrameTimestamps* outFrameTimestamps) const noexcept override; + + +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + + using SurfaceBundle = SurfaceBundle; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; + + VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override; + +private: + struct AndroidDetails; + + struct ExternalImageVulkanAndroid : public ExternalImage { + AHardwareBuffer* aHardwareBuffer = nullptr; + bool sRGB = false; + + protected: + ~ExternalImageVulkanAndroid() override; + }; + + AndroidDetails& mAndroidDetails; + int mOSVersion{}; +}; + +}// namespace filament::backend + +#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H diff --git a/package/ios/libs/filament/include/backend/platforms/VulkanPlatformApple.h b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformApple.h new file mode 100644 index 00000000..5a7c2119 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformApple.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_VULKANPLATFORMAPPLE_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H + +#include + +namespace filament::backend { + +class VulkanPlatformApple : public VulkanPlatform { +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H diff --git a/package/ios/libs/filament/include/backend/platforms/VulkanPlatformLinux.h b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformLinux.h new file mode 100644 index 00000000..2fb964dd --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformLinux.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_VULKANPLATFORMLINUX_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H + +#include + +namespace filament::backend { + +class VulkanPlatformLinux : public VulkanPlatform { +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H diff --git a/package/ios/libs/filament/include/backend/platforms/VulkanPlatformWindows.h b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformWindows.h new file mode 100644 index 00000000..0b55dede --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/VulkanPlatformWindows.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 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_PLATFORMS_VULKANPLATFORMWINDOWS_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H + +#include + +namespace filament::backend { + +class VulkanPlatformWindows : public VulkanPlatform { +protected: + ExtensionSet getSwapchainInstanceExtensions() const override; + SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance, + uint64_t flags) const noexcept override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H diff --git a/package/ios/libs/filament/include/backend/platforms/WebGPUPlatform.h b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatform.h new file mode 100644 index 00000000..ebdb70ac --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatform.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORM_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H + +#include + +#if defined(__linux__) && defined(FILAMENT_SUPPORTS_X11) +// Resolve the conflicts between webgpu_cpp.h and X11 defines +#undef Always +#undef Success +#undef None +#undef True +#undef False +#undef Status +#undef Bool +#endif +#include + +#include +#include + +namespace filament::backend { + +/** + * A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU + * driver (backend). + */ +class WebGPUPlatform : public Platform { +public: + WebGPUPlatform(); + ~WebGPUPlatform() override = default; + + [[nodiscard]] int getOSVersion() const noexcept final { return 0; } + + [[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; } + + // TODO consider that this functionality is not WebGPU-specific, and thus could be + // placed in a generic place and even reused across backends. Alternatively, + // a 3rd party library could be considered. However, this was a simple and + // quick change and works for now. + // gets the size (height and width) of the surface/window + [[nodiscard]] virtual wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const = 0; + // either returns a valid surface or panics + [[nodiscard]] virtual wgpu::Surface createSurface(void* nativeWindow, uint64_t flags) = 0; + // either returns a valid adapter or panics + [[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface); + // either returns a valid device or panics + [[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter); + + struct Configuration { + wgpu::BackendType forceBackendType = wgpu::BackendType::Undefined; + }; + + [[nodiscard]] virtual Configuration getConfiguration() const noexcept { + return {}; + } + +protected: + [[nodiscard]] Driver* createDriver(void* sharedContext, + const Platform::DriverConfig& driverConfig) override; + + // returns adapter request option variations applicable for the particular + // platform + [[nodiscard]] virtual std::vector getAdapterOptions() = 0; + + // we may consider having the driver own this in the future + wgpu::Instance mInstance; +}; + +}// namespace filament::backend + +#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H diff --git a/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformAndroid.h b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformAndroid.h new file mode 100644 index 00000000..fc3f33b1 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformAndroid.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMANDROID_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H + +#include + +namespace filament::backend { + +class WebGPUPlatformAndroid : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H diff --git a/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformApple.h b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformApple.h new file mode 100644 index 00000000..81f9f089 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformApple.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMAPPLE_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H + +#include + +namespace filament::backend { + +class WebGPUPlatformApple : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H diff --git a/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformLinux.h b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformLinux.h new file mode 100644 index 00000000..43ad7af6 --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformLinux.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMLINUX_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H + +#include + +namespace filament::backend { + +class WebGPUPlatformLinux : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H diff --git a/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformWindows.h b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformWindows.h new file mode 100644 index 00000000..cc7f6d9a --- /dev/null +++ b/package/ios/libs/filament/include/backend/platforms/WebGPUPlatformWindows.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2025 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_PLATFORMS_WEBGPUPLATFORMWINDOWS_H +#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H + +#include + +namespace filament::backend { + +class WebGPUPlatformWindows : public WebGPUPlatform { +public: + wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override; + wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override; + +protected: + std::vector getAdapterOptions() override; +}; + +} // namespace filament::backend + +#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H diff --git a/package/ios/libs/filament/include/camutils/Manipulator.h b/package/ios/libs/filament/include/camutils/Manipulator.h index 2cc8a4da..5ea49eaf 100644 --- a/package/ios/libs/filament/include/camutils/Manipulator.h +++ b/package/ios/libs/filament/include/camutils/Manipulator.h @@ -109,6 +109,7 @@ class CAMUTILS_PUBLIC Manipulator { vec4 groundPlane; RayCallback raycastCallback; void* raycastUserdata; + bool panning = true; }; struct Builder { @@ -143,6 +144,8 @@ class CAMUTILS_PUBLIC Manipulator { Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan + Builder& panning(bool enabled); //! Sets whether panning is enabled + /** * Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT. * diff --git a/package/ios/libs/filament/include/filamat/Enums.h b/package/ios/libs/filament/include/filamat/Enums.h deleted file mode 100644 index 04b5ff8b..00000000 --- a/package/ios/libs/filament/include/filamat/Enums.h +++ /dev/null @@ -1,98 +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 -#include -#include - -#include - -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; -using ConstantType = MaterialBuilder::ConstantType; - -// 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 - static bool isValid(const std::string& s) noexcept { - std::unordered_map& map = getMap(); - 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 - static T toEnum(const std::string& s) noexcept { - std::unordered_map& map = getMap(); - return map.at(s); - } - - template - static std::string toString(T t) noexcept; - - // Return a map of all values in an enum with their string representation. - template - static std::unordered_map& map() noexcept { - std::unordered_map& map = getMap(); - return map; - }; - -private: - template - static std::unordered_map& getMap() noexcept; - - static std::unordered_map mStringToProperty; - static std::unordered_map mStringToUniformType; - static std::unordered_map mStringToSamplerType; - static std::unordered_map mStringToSubpassType; - static std::unordered_map mStringToSamplerFormat; - static std::unordered_map mStringToSamplerPrecision; - static std::unordered_map mStringToOutputTarget; - static std::unordered_map mStringToOutputQualifier; - static std::unordered_map mStringToOutputType; - static std::unordered_map mStringToConstantType; -}; - -template -std::string Enums::toString(T t) noexcept { - std::unordered_map& map = getMap(); - 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 diff --git a/package/ios/libs/filament/include/filamat/IncludeCallback.h b/package/ios/libs/filament/include/filamat/IncludeCallback.h deleted file mode 100644 index 659ba289..00000000 --- a/package/ios/libs/filament/include/filamat/IncludeCallback.h +++ /dev/null @@ -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 - -#include - -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; - -} // namespace filamat - -#endif diff --git a/package/ios/libs/filament/include/filamat/MaterialBuilder.h b/package/ios/libs/filament/include/filamat/MaterialBuilder.h deleted file mode 100644 index 4b66965d..00000000 --- a/package/ios/libs/filament/include/filamat/MaterialBuilder.h +++ /dev/null @@ -1,909 +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 - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace utils { -class JobSystem; -} - -namespace filament { -class BufferInterfaceBlock; -} - -namespace filamat { - -struct MaterialInfo; -struct Variant; -class ChunkContainer; - -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). - * When generating the GLSL this is used to differentiate OpenGL from OpenGLES, it is also - * used to make some performance adjustments. - */ - enum class Platform { - DESKTOP, - MOBILE, - ALL - }; - - /** - * TargetApi defines which language after transpilation will be used, it is used to - * account for some differences between these languages when generating the GLSL. - */ - enum class TargetApi : uint8_t { - OPENGL = 0x01u, - VULKAN = 0x02u, - METAL = 0x04u, - ALL = OPENGL | VULKAN | METAL - }; - - /* - * Generally we generate GLSL that will be converted to SPIRV, optimized and then - * transpiled to the backend's language such as MSL, ESSL300, GLSL410 or SPIRV, in this - * case the generated GLSL uses ESSL310 or GLSL450 and has Vulkan semantics and - * TargetLanguage::SPIRV must be used. - * - * However, in some cases (e.g. when no optimization is asked) we generate the *final* GLSL - * directly, this GLSL must be ESSL300 or GLSL410 and cannot use any Vulkan syntax, for this - * situation we use TargetLanguage::GLSL. In this case TargetApi is guaranteed to be OPENGL. - * - * Note that TargetLanguage::GLSL is not the common case, as it is generally not used in - * release builds. - * - * Also note that glslang performs semantics analysis on whichever GLSL ends up being generated. - */ - enum class TargetLanguage { - GLSL, // GLSL with OpenGL 4.1 / OpenGL ES 3.0 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, filament::backend::FeatureLevel featureLevel); - - using ShaderModel = filament::backend::ShaderModel; - Platform mPlatform = Platform::DESKTOP; - TargetApi mTargetApi = (TargetApi) 0; - Optimization mOptimization = Optimization::PERFORMANCE; - bool mPrintShaders = false; - bool mGenerateDebugInfo = false; - bool mIncludeEssl1 = true; - utils::bitset32 mShaderModels; - struct CodeGenParams { - ShaderModel shaderModel; - TargetApi targetApi; - TargetLanguage targetLanguage; - filament::backend::FeatureLevel featureLevel; - }; - std::vector mCodeGenPermutations; - - // 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 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 - * 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(); - ~MaterialBuilder(); - - MaterialBuilder(const MaterialBuilder& rhs) = delete; - MaterialBuilder& operator=(const MaterialBuilder& rhs) = delete; - - MaterialBuilder(MaterialBuilder&& rhs) noexcept = default; - MaterialBuilder& operator=(MaterialBuilder&& rhs) noexcept = default; - - 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 ReflectionMode = filament::ReflectionMode; - using VertexAttribute = filament::VertexAttribute; - - using ShaderQuality = filament::ShaderQuality; - using BlendingMode = filament::BlendingMode; - using BlendFunction = filament::backend::BlendFunction; - using Shading = filament::Shading; - using Interpolation = filament::Interpolation; - using VertexDomain = filament::VertexDomain; - using TransparencyMode = filament::TransparencyMode; - using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion; - - using AttributeType = filament::backend::UniformType; - using UniformType = filament::backend::UniformType; - using ConstantType = filament::backend::ConstantType; - using SamplerType = filament::backend::SamplerType; - using SubpassType = filament::backend::SubpassType; - using SamplerFormat = filament::backend::SamplerFormat; - using ParameterPrecision = filament::backend::Precision; - using Precision = filament::backend::Precision; - using CullingMode = filament::backend::CullingMode; - using FeatureLevel = filament::backend::FeatureLevel; - using StereoscopicType = filament::backend::StereoscopicType; - - 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(std::string name, std::string value) : - name(std::move(name)), value(std::move(value)) {} - }; - using PreprocessorDefineList = std::vector; - - - MaterialBuilder& noSamplerValidation(bool enabled) noexcept; - - //! Enable generation of ESSL 1.0 code in FL0 materials. - MaterialBuilder& includeEssl1(bool enabled) noexcept; - - //! 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(const char* name, UniformType type, - ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept; - - //! Add a parameter array to this material. - MaterialBuilder& parameter(const char* name, size_t size, UniformType type, - ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept; - - //! Add a constant parameter to this material. - template - using is_supported_constant_parameter_t = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value>::type; - template> - MaterialBuilder& constant(const char *name, ConstantType type, T defaultValue = 0); - - /** - * Add a sampler parameter to this material. - * - * When SamplerType::SAMPLER_EXTERNAL is specified, format and precision are ignored. - */ - MaterialBuilder& parameter(const char* name, SamplerType samplerType, - SamplerFormat format = SamplerFormat::FLOAT, - ParameterPrecision precision = ParameterPrecision::DEFAULT, - bool multisample = false) noexcept; - - MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) 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(VertexAttribute attribute) noexcept; - - //! Specify the domain that this material will operate in. - MaterialBuilder& materialDomain(MaterialBuilder::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; - - MaterialBuilder& featureLevel(FeatureLevel featureLevel) noexcept; - - /** - * Set the blending mode for this material. When set to MASKED, alpha to coverage is turned on. - * You can override this behavior using alphaToCoverage(false). - */ - MaterialBuilder& blending(BlendingMode blending) noexcept; - - /** - * Set the blend function for this material. blending must be et to CUSTOM. - */ - MaterialBuilder& customBlendFunctions( - BlendFunction srcRGB, - BlendFunction srcA, - BlendFunction dstRGB, - BlendFunction dstA) 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 properties "postLightingColor" and - * "postLightingMixFactor" 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; - - //! Enable / disable instanced primitives (disabled by default). - MaterialBuilder& instanced(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; - - /** - * Enables or disables alpha-to-coverage. When enabled, the coverage of a fragment is based - * on its alpha value. This parameter is only useful when MSAA is in use. Alpha to coverage - * is enabled automatically when the blend mode is set to MASKED; this behavior can be - * overridden by calling alphaToCoverage(false). - */ - MaterialBuilder& alphaToCoverage(bool enable) 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 reflections should be rendered (default is DEFAULT). - MaterialBuilder& reflectionMode(ReflectionMode mode) noexcept; - - //! Specifies how transparent objects should be rendered (default is DEFAULT). - MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept; - - //! Specify the stereoscopic type (default is INSTANCED) - MaterialBuilder& stereoscopicType(StereoscopicType stereoscopicType) noexcept; - - //! Specify the number of eyes for stereoscopic rendering - MaterialBuilder& stereoscopicEyeCount(uint8_t eyeCount) 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(filament::UserVariantFilterMask 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, Precision precision, - OutputType type, const char* name, int location = -1) noexcept; - - MaterialBuilder& enableFramebufferFetch() noexcept; - - MaterialBuilder& vertexDomainDeviceJittered(bool enabled) noexcept; - - /** - * Legacy morphing uses the data in the VertexAttribute slots (\c MORPH_POSITION_0, etc) and is - * limited to 4 morph targets. See filament::RenderableManager::Builder::morphing(). - */ - MaterialBuilder& useLegacyMorphing() noexcept; - - //! specify compute kernel group size - MaterialBuilder& groupSize(filament::math::uint3 groupSize) 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& subpass(SubpassType subpassType, - SamplerFormat format, ParameterPrecision precision, const char* name) noexcept; - MaterialBuilder& subpass(SubpassType subpassType, - SamplerFormat format, const char* name) noexcept; - MaterialBuilder& subpass(SubpassType subpassType, - ParameterPrecision precision, const char* name) noexcept; - MaterialBuilder& subpass(SubpassType subpassType, const char* name) noexcept; - - struct Parameter { - Parameter() noexcept: parameterType(INVALID) {} - - // Sampler - Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms) - : name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms) { } - - // 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; - bool multisample; - 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, - Precision precision, OutputType type, int location) noexcept - : name(outputName), qualifier(qualifier), target(target), precision(precision), - type(type), location(location) { } - - utils::CString name; - VariableQualifier qualifier; - OutputTarget target; - Precision precision; - OutputType type; - int location; - }; - - struct Constant { - utils::CString name; - ConstantType type; - union { - int32_t i; - float f; - bool b; - } defaultValue; - }; - - 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; - - 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::MAX_SUPPORTED_RENDER_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. - std::string peek(filament::backend::ShaderStage type, - const CodeGenParams& params, const PropertyList& properties) noexcept; - - // Returns true if any of the parameter samplers matches the specified type. - bool hasSamplerType(SamplerType samplerType) const noexcept; - - static constexpr size_t MAX_PARAMETERS_COUNT = 48; - static constexpr size_t MAX_SUBPASS_COUNT = 1; - static constexpr size_t MAX_BUFFERS_COUNT = 4; - using ParameterList = Parameter[MAX_PARAMETERS_COUNT]; - using SubpassList = Parameter[MAX_SUBPASS_COUNT]; - using BufferList = std::vector>; - using ConstantList = std::vector; - - // 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; } - - // returns the number of parameters declared in this material - uint8_t getSubpassCount() const noexcept { return mSubpassCount; } - - // returns a list of at least getParameterCount() parameters - const SubpassList& getSubPasses() const noexcept { return mSubpasses; } - - filament::UserVariantFilterMask getVariantFilter() const { return mVariantFilter; } - - FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; } - /// @endcond - - struct Attribute { - std::string_view name; - AttributeType type; - MaterialBuilder::VertexAttribute location; - std::string getAttributeName() const noexcept { - return "mesh_" + std::string{ name }; - } - std::string getDefineName() const noexcept { - std::string uppercase{ name }; - transform(uppercase.cbegin(), uppercase.cend(), uppercase.begin(), ::toupper); - return "HAS_ATTRIBUTE_" + uppercase; - } - }; - - using AttributeDatabase = std::array; - - static inline AttributeDatabase const& getAttributeDatabase() noexcept { - return sAttributeDatabase; - } - -private: - static const AttributeDatabase sAttributeDatabase; - - 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(CodeGenParams const& semanticCodeGenParams) noexcept; - - // Multiple calls to findProperties accumulate the property sets across fragment - // and vertex shaders in mProperties. - bool findProperties(filament::backend::ShaderStage type, - MaterialBuilder::PropertyList& allProperties, - CodeGenParams const& semanticCodeGenParams) noexcept; - - bool runSemanticAnalysis(MaterialInfo* inOutInfo, - CodeGenParams const& semanticCodeGenParams) noexcept; - - bool checkLiteRequirements() noexcept; - - bool checkMaterialLevelFeatures(MaterialInfo const& info) const noexcept; - - void writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept; - void writeSurfaceChunks(ChunkContainer& container) const noexcept; - - bool generateShaders( - utils::JobSystem& jobSystem, - const std::vector& variants, ChunkContainer& container, - const MaterialInfo& info) const noexcept; - - bool hasCustomVaryings() const noexcept; - bool needsStandardDepthProgram() 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 mMaterialFragmentCode; - ShaderCode mMaterialVertexCode; - - IncludeCallback mIncludeCallback = nullptr; - - PropertyList mProperties; - ParameterList mParameters; - ConstantList mConstants; - SubpassList mSubpasses; - VariableList mVariables; - OutputList mOutputs; - BufferList mBuffers; - - ShaderQuality mShaderQuality = ShaderQuality::DEFAULT; - FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1; - BlendingMode mBlendingMode = BlendingMode::OPAQUE; - BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT; - std::array mCustomBlendFunctions = {}; - CullingMode mCullingMode = CullingMode::BACK; - Shading mShading = Shading::LIT; - MaterialDomain mMaterialDomain = MaterialDomain::SURFACE; - RefractionMode mRefractionMode = RefractionMode::NONE; - RefractionType mRefractionType = RefractionType::SOLID; - ReflectionMode mReflectionMode = ReflectionMode::DEFAULT; - Interpolation mInterpolation = Interpolation::SMOOTH; - VertexDomain mVertexDomain = VertexDomain::OBJECT; - TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT; - StereoscopicType mStereoscopicType = StereoscopicType::INSTANCED; - uint8_t mStereoscopicEyeCount = 2; - - filament::AttributeBitset mRequiredAttributes; - - float mMaskThreshold = 0.4f; - float mSpecularAntiAliasingVariance = 0.15f; - float mSpecularAntiAliasingThreshold = 0.2f; - - filament::math::uint3 mGroupSize = { 1, 1, 1 }; - - bool mShadowMultiplier = false; - bool mTransparentShadow = false; - - uint8_t mParameterCount = 0; - uint8_t mSubpassCount = 0; - - bool mDoubleSided = false; - bool mDoubleSidedCapability = false; - bool mColorWrite = true; - bool mDepthTest = true; - bool mInstanced = false; - bool mDepthWrite = true; - bool mDepthWriteSet = false; - bool mAlphaToCoverage = false; - bool mAlphaToCoverageSet = 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; - - bool mVertexDomainDeviceJittered = false; - - bool mUseLegacyMorphing = false; - - PreprocessorDefineList mDefines; - - filament::UserVariantFilterMask mVariantFilter = {}; - - bool mNoSamplerValidation = false; -}; - -} // namespace filamat - -template<> struct utils::EnableBitMaskOperators - : public std::true_type {}; - -#endif diff --git a/package/ios/libs/filament/include/filamat/Package.h b/package/ios/libs/filament/include/filamat/Package.h deleted file mode 100644 index 93e74a58..00000000 --- a/package/ios/libs/filament/include/filamat/Package.h +++ /dev/null @@ -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 -#include -#include - -#include -#include - -#include - -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 diff --git a/package/ios/libs/filament/include/filament-generatePrefilterMipmap/generatePrefilterMipmap.h b/package/ios/libs/filament/include/filament-generatePrefilterMipmap/generatePrefilterMipmap.h new file mode 100644 index 00000000..ab506c9f --- /dev/null +++ b/package/ios/libs/filament/include/filament-generatePrefilterMipmap/generatePrefilterMipmap.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 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 + +#include +#include + +namespace filament { + +namespace backend { +class PixelBufferDescriptor; +} + +class Engine; +class Texture; + +struct UTILS_PUBLIC FaceOffsets { + using size_type = size_t; + union { + struct { + size_type px; //!< +x face offset in bytes + size_type nx; //!< -x face offset in bytes + size_type py; //!< +y face offset in bytes + size_type ny; //!< -y face offset in bytes + size_type pz; //!< +z face offset in bytes + size_type nz; //!< -z face offset in bytes + }; + size_type offsets[6]; + }; + size_type operator[](size_t const n) const noexcept { return offsets[n]; } + size_type& operator[](size_t const n) { return offsets[n]; } + FaceOffsets() noexcept = default; + explicit FaceOffsets(size_type const faceSize) noexcept { + px = faceSize * 0; + nx = faceSize * 1; + py = faceSize * 2; + ny = faceSize * 3; + pz = faceSize * 4; + nz = faceSize * 5; + } + FaceOffsets(const FaceOffsets& rhs) noexcept { + px = rhs.px; + nx = rhs.nx; + py = rhs.py; + ny = rhs.ny; + pz = rhs.pz; + nz = rhs.nz; + } + FaceOffsets& operator=(const FaceOffsets& rhs) noexcept { + px = rhs.px; + nx = rhs.nx; + py = rhs.py; + ny = rhs.ny; + pz = rhs.pz; + nz = rhs.nz; + return *this; + } +}; + +/** + * Options for environment prefiltering into reflection map + * + * @see generatePrefilterMipmap() + */ +struct UTILS_PUBLIC PrefilterOptions { + uint16_t sampleCount = 8; //!< sample count used for filtering + bool mirror = true; //!< whether the environment must be mirrored +private: + UTILS_UNUSED uintptr_t reserved[3] = {}; +}; + +UTILS_PUBLIC +void generatePrefilterMipmap(Texture* UTILS_NONNULL texture, Engine& engine, + backend::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, + PrefilterOptions const* UTILS_NULLABLE options = nullptr); + + +} // namespace filament diff --git a/package/ios/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h b/package/ios/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h index 903c4258..e894e148 100644 --- a/package/ios/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h +++ b/package/ios/libs/filament/include/filament-iblprefilter/IBLPrefilterContext.h @@ -227,7 +227,6 @@ class UTILS_PUBLIC IBLPrefilterContext { filament::Texture* outIrradianceTexture = nullptr); private: - filament::Texture* createIrradianceTexture(); IBLPrefilterContext& mContext; filament::Material* mKernelMaterial = nullptr; filament::Texture* mKernelTexture = nullptr; @@ -325,7 +324,6 @@ class UTILS_PUBLIC IBLPrefilterContext { // TODO: add a callback for when the processing is done? private: - filament::Texture* createReflectionsTexture(); IBLPrefilterContext& mContext; filament::Material* mKernelMaterial = nullptr; filament::Texture* mKernelTexture = nullptr; diff --git a/package/ios/libs/filament/include/filament/Box.h b/package/ios/libs/filament/include/filament/Box.h index da6638da..35042030 100644 --- a/package/ios/libs/filament/include/filament/Box.h +++ b/package/ios/libs/filament/include/filament/Box.h @@ -120,8 +120,11 @@ class UTILS_PUBLIC Box { } /** - * @deprecated Use transform() instead - * @see transform() + * Transform a Box by a linear transform and a translation. + * + * @param m a linear transform matrix + * @param box the box to transform + * @return the bounding box of the transformed box */ friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept { return transform(m.upperLeft(), m[3].xyz, box); @@ -183,7 +186,7 @@ struct UTILS_PUBLIC Aabb { * Returns the 8 corner vertices of the AABB. */ Corners getCorners() const { - return Aabb::Corners{ .vertices = { + return Corners{ .vertices = { { min.x, min.y, min.z }, { max.x, min.y, min.z }, { min.x, max.y, min.z }, @@ -235,8 +238,10 @@ struct UTILS_PUBLIC Aabb { } /** - * @deprecated Use transform() instead - * @see transform() + * Applies an affine transformation to the AABB. + * + * @param m the affine transformation to apply + * @return the bounding box of the transformed box */ Aabb transform(const math::mat4f& m) const noexcept { return transform(m.upperLeft(), m[3].xyz, *this); diff --git a/package/ios/libs/filament/include/filament/BufferObject.h b/package/ios/libs/filament/include/filament/BufferObject.h index 74a4b1ff..bf3ffe3d 100644 --- a/package/ios/libs/filament/include/filament/BufferObject.h +++ b/package/ios/libs/filament/include/filament/BufferObject.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -54,7 +55,7 @@ class UTILS_PUBLIC BufferObject : public FilamentAPI { using BufferDescriptor = backend::BufferDescriptor; using BindingType = backend::BufferObjectBinding; - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -73,11 +74,38 @@ class UTILS_PUBLIC BufferObject : public FilamentAPI { /** * The binding type for this buffer object. (defaults to VERTEX) - * @param BindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. + * @param bindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. * @return A reference to this Builder for chaining calls. */ Builder& bindingType(BindingType bindingType) noexcept; + /** + * Associate an optional name with this BufferObject for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this BufferObject + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this BufferObject for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this BufferObject + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the BufferObject and returns a pointer to it. After creation, the buffer * object is uninitialized. Use BufferObject::setBuffer() to initialize it. @@ -102,7 +130,7 @@ class UTILS_PUBLIC BufferObject : public FilamentAPI { * * @param engine Reference to the filament::Engine associated with this BufferObject. * @param buffer A BufferDescriptor representing the data used to initialize the BufferObject. - * @param byteOffset Offset in bytes into the BufferObject + * @param byteOffset Offset in bytes into the BufferObject. Must be multiple of 4. */ void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); diff --git a/package/ios/libs/filament/include/filament/Camera.h b/package/ios/libs/filament/include/filament/Camera.h index 74f34af0..abf8b2c1 100644 --- a/package/ios/libs/filament/include/filament/Camera.h +++ b/package/ios/libs/filament/include/filament/Camera.h @@ -61,7 +61,7 @@ namespace filament { * filament::Camera* myCamera = engine->createCamera(myCameraEntity); * myCamera->setProjection(45, 16.0/9.0, 0.1, 1.0); * myCamera->lookAt({0, 1.60, 1}, {0, 0, 0}); - * engine->destroyCameraComponent(myCamera); + * engine->destroyCameraComponent(myCameraEntity); * ~~~~~~~~~~~ * * @@ -243,10 +243,10 @@ class UTILS_PUBLIC Camera : public FilamentAPI { /** Utility to set the projection matrix from the field-of-view. * * @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180. - * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. - * @param direction direction of the \p fovInDegrees parameter. + * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param direction direction of the \p fovInDegrees parameter. * * @see Fov. */ @@ -256,9 +256,9 @@ class UTILS_PUBLIC Camera : public FilamentAPI { /** Utility to set the projection matrix from the focal length. * * @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0. - * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. */ void setLensProjection(double focalLengthInMillimeters, double aspect, double near, double far); @@ -270,8 +270,8 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * that is all 3 axis are mapped to [-1, 1]. * * @param projection custom projection matrix used for rendering and culling - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param near distance in world units from the camera to the near plane. + * @param far distance in world units from the camera to the far plane. \p far != \p near. */ void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept; @@ -282,8 +282,8 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * * @param projection custom projection matrix used for rendering * @param projectionForCulling custom projection matrix used for culling - * @param near distance in world units from the camera to the near plane. \p near > 0. - * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param near distance in world units from the camera to the near plane. + * @param far distance in world units from the camera to the far plane. \p far != \p near. */ void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling, double near, double far) noexcept; @@ -400,14 +400,14 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * engine.getTransformManager().getInstance(camera->getEntity()), model); * ~~~~~~~~~~~ * - * @param model The camera position and orientation provided as a rigid transform matrix. + * @param modelMatrix The camera position and orientation provided as a rigid transform matrix. * * @note The Camera "looks" towards its -z axis * * @warning \p model must be a rigid transform */ - void setModelMatrix(const math::mat4& model) noexcept; - void setModelMatrix(const math::mat4f& model) noexcept; //!< @overload + void setModelMatrix(const math::mat4& modelMatrix) noexcept; + void setModelMatrix(const math::mat4f& modelMatrix) noexcept; //!< @overload /** Set the position of an eye relative to this Camera (head). * diff --git a/package/ios/libs/filament/include/filament/Color.h b/package/ios/libs/filament/include/filament/Color.h index 30b77856..37939820 100644 --- a/package/ios/libs/filament/include/filament/Color.h +++ b/package/ios/libs/filament/include/filament/Color.h @@ -194,7 +194,7 @@ inline sRGBColorA Color::toSRGB(LinearColorA const& color) { } inline LinearColor Color::toLinear(RgbType type, math::float3 color) { - return (type == RgbType::LINEAR) ? color : Color::toLinear(color); + return (type == RgbType::LINEAR) ? color : toLinear(color); } // converts an RGBA color to linear space @@ -202,11 +202,11 @@ inline LinearColor Color::toLinear(RgbType type, math::float3 color) { inline LinearColorA Color::toLinear(RgbaType type, math::float4 color) { switch (type) { case RgbaType::sRGB: - return Color::toLinear(color) * math::float4{color.a, color.a, color.a, 1.0f}; + return toLinear(color) * math::float4{color.a, color.a, color.a, 1.0f}; case RgbaType::LINEAR: return color * math::float4{color.a, color.a, color.a, 1.0f}; case RgbaType::PREMULTIPLIED_sRGB: - return Color::toLinear(color); + return toLinear(color); case RgbaType::PREMULTIPLIED_LINEAR: return color; } diff --git a/package/ios/libs/filament/include/filament/ColorGrading.h b/package/ios/libs/filament/include/filament/ColorGrading.h index e5c8f3ca..3e4916fb 100644 --- a/package/ios/libs/filament/include/filament/ColorGrading.h +++ b/package/ios/libs/filament/include/filament/ColorGrading.h @@ -64,9 +64,8 @@ class ColorSpace; * Performance * =========== * - * Creating a new ColorGrading object may be more expensive than other Filament objects as a - * 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on - * the CPU. + * Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may + * need to be generated. The generation of this LUT, if necessary, may happen on the CPU. * * Ordering * ======== @@ -155,6 +154,9 @@ class UTILS_PUBLIC ColorGrading : public FilamentAPI { * 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium * quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit * LUT, and a ultra quality will use a 64x64x64 16 bit LUT. + * + * This setting has no effect if generating a 1D LUT. + * * This overrides the values set by format() and dimensions(). * * The default quality is medium. @@ -169,6 +171,8 @@ class UTILS_PUBLIC ColorGrading : public FilamentAPI { * When color grading is implemented using a 3D LUT, this sets the texture format of * of the LUT. This overrides the value set by quality(). * + * This setting has no effect if generating a 1D LUT. + * * The default is INTEGER * * @param format The desired format of the 3D LUT. @@ -181,6 +185,8 @@ class UTILS_PUBLIC ColorGrading : public FilamentAPI { * When color grading is implemented using a 3D LUT, this sets the dimension of the LUT. * This overrides the value set by quality(). * + * This setting has no effect if generating a 1D LUT. + * * The default is 32 * * @param dim The desired dimension of the LUT. Between 16 and 64. diff --git a/package/ios/libs/filament/include/filament/Engine.h b/package/ios/libs/filament/include/filament/Engine.h index b741d3ab..c9dee81c 100644 --- a/package/ios/libs/filament/include/filament/Engine.h +++ b/package/ios/libs/filament/include/filament/Engine.h @@ -17,6 +17,7 @@ #ifndef TNT_FILAMENT_ENGINE_H #define TNT_FILAMENT_ENGINE_H + #include #include @@ -24,10 +25,16 @@ #include #include +#include + +#include +#include +#include #include #include + namespace utils { class Entity; class EntityManager; @@ -36,6 +43,10 @@ class JobSystem; namespace filament { +namespace backend { +class Driver; +} // backend + class BufferObject; class Camera; class ColorGrading; @@ -53,6 +64,7 @@ class Scene; class Skybox; class Stream; class SwapChain; +class Sync; class Texture; class VertexBuffer; class View; @@ -179,6 +191,11 @@ class UTILS_PUBLIC Engine { using DriverConfig = backend::Platform::DriverConfig; using FeatureLevel = backend::FeatureLevel; using StereoscopicType = backend::StereoscopicType; + using Driver = backend::Driver; + using GpuContextPriority = backend::Platform::GpuContextPriority; + using AsynchronousMode = backend::AsynchronousMode; + using AsyncCompletionCallback = std::function; + using AsyncCallId = backend::AsyncCallId; /** * Config is used to define the memory footprint used by the engine, such as the @@ -288,19 +305,37 @@ class UTILS_PUBLIC Engine { */ uint32_t jobSystemThreadCount = 0; - /* - * Number of most-recently destroyed textures to track for use-after-free. + /** + * When uploading vertex or index data, the Filament Metal backend copies data + * into a shared staging area before transferring it to the GPU. This setting controls + * the total size of the buffer used to perform these allocations. + * + * Higher values can improve performance when performing many uploads across a small + * number of frames. + * + * This buffer remains alive throughout the lifetime of the Engine, so this size adds to the + * memory footprint of the app and should be set as conservative as possible. * - * This will cause the backend to throw an exception when a texture is freed but still bound - * to a SamplerGroup and used in a draw call. 0 disables completely. + * A value of 0 disables the shared staging buffer entirely; uploads will acquire an + * individual buffer from a pool of shared buffers. * - * Currently only respected by the Metal backend. + * Only respected by the Metal backend. */ - size_t textureUseAfterFreePoolSize = 0; + size_t metalUploadBufferSizeBytes = 512 * 1024; + + /** + * The action to take if a Drawable cannot be acquired. + * + * Each frame rendered requires a CAMetalDrawable texture, which is + * presented on-screen at the completion of each frame. These are + * limited and provided round-robin style by the system. + */ + bool metalDisablePanicOnDrawableFailure = false; /** * Set to `true` to forcibly disable parallel shader compilation in the backend. * Currently only honored by the GL and Metal backends. + * @deprecated use "backend.disable_parallel_shader_compile" feature flag instead */ bool disableParallelShaderCompile = false; @@ -315,7 +350,7 @@ class UTILS_PUBLIC Engine { * * @see View::setStereoscopicOptions */ - StereoscopicType stereoscopicType = StereoscopicType::INSTANCED; + StereoscopicType stereoscopicType = StereoscopicType::NONE; /* * The number of eyes to render when stereoscopic rendering is enabled. Supported values are @@ -332,17 +367,103 @@ class UTILS_PUBLIC Engine { uint32_t resourceAllocatorCacheSizeMB = 64; /* - * This value determines for how many frames are texture entries kept in the cache. + * This value determines how many frames texture entries are kept for in the cache. This + * is a soft limit, meaning some texture older than this are allowed to stay in the cache. + * Typically only one texture is evicted per frame. + * The default is 1. */ - uint32_t resourceAllocatorCacheMaxAge = 2; + uint32_t resourceAllocatorCacheMaxAge = 1; /* * Disable backend handles use-after-free checks. + * @deprecated use "backend.disable_handle_use_after_free_check" feature flag instead */ bool disableHandleUseAfterFreeCheck = false; + + /* + * Sets a preferred shader language for Filament to use. + * + * The Metal backend supports two shader languages: MSL (Metal Shading Language) and + * METAL_LIBRARY (precompiled .metallib). This option controls which shader language is + * used when materials contain both. + * + * By default, when preferredShaderLanguage is unset, Filament will prefer METAL_LIBRARY + * shaders if present within a material, falling back to MSL. Setting + * preferredShaderLanguage to ShaderLanguage::MSL will instead instruct Filament to check + * for the presence of MSL in a material first, falling back to METAL_LIBRARY if MSL is not + * present. + * + * When using a non-Metal backend, setting this has no effect. + */ + enum class ShaderLanguage { + DEFAULT = 0, + MSL = 1, + METAL_LIBRARY = 2, + }; + ShaderLanguage preferredShaderLanguage = ShaderLanguage::DEFAULT; + + /* + * When the OpenGL ES backend is used, setting this value to true will force a GLES2.0 + * context if supported by the Platform, or if not, will have the backend pretend + * it's a GLES2 context. Ignored on other backends. + */ + bool forceGLES2Context = false; + + /** + * Assert the native window associated to a SwapChain is valid when calling makeCurrent(). + * This is only supported for: + * - PlatformEGLAndroid + * @deprecated use "backend.opengl.assert_native_window_is_valid" feature flag instead + */ + bool assertNativeWindowIsValid = false; + + /** + * GPU context priority level. Controls GPU work scheduling and preemption. + */ + GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT; + + /** + * The initial size in bytes of the shared uniform buffer used for material instance + * batching. + * + * If the buffer runs out of space during a frame, it will be automatically reallocated + * with a larger capacity. Setting an appropriate initial size can help avoid runtime + * reallocations, which can cause a minor performance stutter, at the cost of higher + * initial memory usage. + */ + uint32_t sharedUboInitialSizeInBytes = 256 * 64; + + /** + * Asynchronous mode for the engine. Defines how asynchronous operations are handled. + * Note that selecting a non-NONE mode does not guarantee asynchronous methods are + * supported, as the underlying backend or the feature flag may override this configuration. + * Always validate availability via Engine::isAsynchronousModeEnabled() before + * invoking asynchronous methods. + */ + AsynchronousMode asynchronousMode = AsynchronousMode::NONE; }; + /** + * Feature flags can be enabled or disabled when the Engine is built. Some Feature flags can + * also be toggled at any time. Feature flags should alawys use their default value unless + * the feature enabled by the flag is faulty. Feature flags provide a last resort way to + * disable problematic features. + * Feature flags are intended to have a short life-time and are regularly removed as features + * mature. + */ + struct FeatureFlag { + char const* UTILS_NONNULL name; //!< name of the feature flag + char const* UTILS_NONNULL description; //!< short description + bool const* UTILS_NONNULL value; //!< pointer to the value of the flag + bool constant = true; //!< whether the flag is constant after construction + }; + + /** + * Returns the list of available feature flags + */ + utils::Slice getFeatureFlags() const noexcept; + #if UTILS_HAS_THREADING using CreateCallback = void(void* UTILS_NULLABLE user, void* UTILS_NONNULL token); #endif @@ -415,6 +536,21 @@ class UTILS_PUBLIC Engine { */ Builder& paused(bool paused) noexcept; + /** + * Set a feature flag value. This is the only way to set constant feature flags. + * @param name feature name + * @param value true to enable, false to disable + * @return A reference to this Builder for chaining calls. + */ + Builder& feature(char const* UTILS_NONNULL name, bool value) noexcept; + + /** + * Enables a list of features. + * @param list list of feature names to enable. + * @return A reference to this Builder for chaining calls. + */ + Builder& features(std::initializer_list list) noexcept; + #if UTILS_HAS_THREADING /** * Creates the filament Engine asynchronously. @@ -448,7 +584,7 @@ class UTILS_PUBLIC Engine { Platform* UTILS_NULLABLE platform = nullptr, void* UTILS_NULLABLE sharedContext = nullptr, const Config* UTILS_NULLABLE config = nullptr) { - return Engine::Builder() + return Builder() .backend(backend) .platform(platform) .sharedContext(sharedContext) @@ -468,7 +604,7 @@ class UTILS_PUBLIC Engine { Platform* UTILS_NULLABLE platform = nullptr, void* UTILS_NULLABLE sharedContext = nullptr, const Config* UTILS_NULLABLE config = nullptr) { - Engine::Builder() + Builder() .backend(backend) .platform(platform) .sharedContext(sharedContext) @@ -493,6 +629,11 @@ class UTILS_PUBLIC Engine { static Engine* UTILS_NULLABLE getEngine(void* UTILS_NONNULL token); #endif + /** + * @return the Driver instance used by this Engine. + * @see OpenGLPlatform + */ + backend::Driver const* UTILS_NONNULL getDriver() const noexcept; /** * Destroy the Engine instance and all associated resources. @@ -614,6 +755,14 @@ class UTILS_PUBLIC Engine { */ bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept; + /** + * Checks if the engine is set up for asynchronous operation. If it returns true, the + * asynchronous versions of the APIs are available for use. + * + * @return true if the engine supports asynchronous operation. + */ + bool isAsynchronousModeEnabled() const noexcept; + /** * Retrieves the configuration settings of this Engine. * @@ -770,9 +919,19 @@ class UTILS_PUBLIC Engine { */ Fence* UTILS_NONNULL createFence() noexcept; + /** + * Creates a Sync. + * @param callback A callback that will be invoked when the handle for + * the created sync is set + * + * @return A pointer to the newly created Sync. + */ + Sync* UTILS_NONNULL createSync() noexcept; + bool destroy(const BufferObject* UTILS_NULLABLE p); //!< Destroys a BufferObject object. bool destroy(const VertexBuffer* UTILS_NULLABLE p); //!< Destroys an VertexBuffer object. bool destroy(const Fence* UTILS_NULLABLE p); //!< Destroys a Fence object. + bool destroy(const Sync* UTILS_NULLABLE p); //!< Destroys a Sync object. bool destroy(const IndexBuffer* UTILS_NULLABLE p); //!< Destroys an IndexBuffer object. bool destroy(const SkinningBuffer* UTILS_NULLABLE p); //!< Destroys a SkinningBuffer object. bool destroy(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Destroys a MorphTargetBuffer object. @@ -800,24 +959,117 @@ class UTILS_PUBLIC Engine { bool destroy(const InstanceBuffer* UTILS_NULLABLE p); //!< Destroys an InstanceBuffer object. void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity - bool isValid(const BufferObject* UTILS_NULLABLE p); //!< Tells whether a BufferObject object is valid - bool isValid(const VertexBuffer* UTILS_NULLABLE p); //!< Tells whether an VertexBuffer object is valid - bool isValid(const Fence* UTILS_NULLABLE p); //!< Tells whether a Fence object is valid - bool isValid(const IndexBuffer* UTILS_NULLABLE p); //!< Tells whether an IndexBuffer object is valid - bool isValid(const SkinningBuffer* UTILS_NULLABLE p); //!< Tells whether a SkinningBuffer object is valid - bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p); //!< Tells whether a MorphTargetBuffer object is valid - bool isValid(const IndirectLight* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid - bool isValid(const Material* UTILS_NULLABLE p); //!< Tells whether an IndirectLight object is valid - bool isValid(const Renderer* UTILS_NULLABLE p); //!< Tells whether a Renderer object is valid - bool isValid(const Scene* UTILS_NULLABLE p); //!< Tells whether a Scene object is valid - bool isValid(const Skybox* UTILS_NULLABLE p); //!< Tells whether a SkyBox object is valid - bool isValid(const ColorGrading* UTILS_NULLABLE p); //!< Tells whether a ColorGrading object is valid - bool isValid(const SwapChain* UTILS_NULLABLE p); //!< Tells whether a SwapChain object is valid - bool isValid(const Stream* UTILS_NULLABLE p); //!< Tells whether a Stream object is valid - bool isValid(const Texture* UTILS_NULLABLE p); //!< Tells whether a Texture object is valid - bool isValid(const RenderTarget* UTILS_NULLABLE p); //!< Tells whether a RenderTarget object is valid - bool isValid(const View* UTILS_NULLABLE p); //!< Tells whether a View object is valid - bool isValid(const InstanceBuffer* UTILS_NULLABLE p); //!< Tells whether an InstanceBuffer object is valid + /** Tells whether a BufferObject object is valid */ + bool isValid(const BufferObject* UTILS_NULLABLE p) const; + /** Tells whether an VertexBuffer object is valid */ + bool isValid(const VertexBuffer* UTILS_NULLABLE p) const; + /** Tells whether a Fence object is valid */ + bool isValid(const Fence* UTILS_NULLABLE p) const; + /** Tells whether a Sync object is valid */ + bool isValid(const Sync* UTILS_NULLABLE p) const; + /** Tells whether an IndexBuffer object is valid */ + bool isValid(const IndexBuffer* UTILS_NULLABLE p) const; + /** Tells whether a SkinningBuffer object is valid */ + bool isValid(const SkinningBuffer* UTILS_NULLABLE p) const; + /** Tells whether a MorphTargetBuffer object is valid */ + bool isValid(const MorphTargetBuffer* UTILS_NULLABLE p) const; + /** Tells whether an IndirectLight object is valid */ + bool isValid(const IndirectLight* UTILS_NULLABLE p) const; + /** Tells whether an Material object is valid */ + bool isValid(const Material* UTILS_NULLABLE p) const; + /** Tells whether an MaterialInstance object is valid. Use this if you already know + * which Material this MaterialInstance belongs to. DO NOT USE getMaterial(), this would + * defeat the purpose of validating the MaterialInstance. + */ + bool isValid(const Material* UTILS_NONNULL m, const MaterialInstance* UTILS_NULLABLE p) const; + /** Tells whether an MaterialInstance object is valid. Use this if the Material the + * MaterialInstance belongs to is not known. This method can be expensive. + */ + bool isValidExpensive(const MaterialInstance* UTILS_NULLABLE p) const; + /** Tells whether a Renderer object is valid */ + bool isValid(const Renderer* UTILS_NULLABLE p) const; + /** Tells whether a Scene object is valid */ + bool isValid(const Scene* UTILS_NULLABLE p) const; + /** Tells whether a SkyBox object is valid */ + bool isValid(const Skybox* UTILS_NULLABLE p) const; + /** Tells whether a ColorGrading object is valid */ + bool isValid(const ColorGrading* UTILS_NULLABLE p) const; + /** Tells whether a SwapChain object is valid */ + bool isValid(const SwapChain* UTILS_NULLABLE p) const; + /** Tells whether a Stream object is valid */ + bool isValid(const Stream* UTILS_NULLABLE p) const; + /** Tells whether a Texture object is valid */ + bool isValid(const Texture* UTILS_NULLABLE p) const; + /** Tells whether a RenderTarget object is valid */ + bool isValid(const RenderTarget* UTILS_NULLABLE p) const; + /** Tells whether a View object is valid */ + bool isValid(const View* UTILS_NULLABLE p) const; + /** Tells whether an InstanceBuffer object is valid */ + bool isValid(const InstanceBuffer* UTILS_NULLABLE p) const; + + /** + * Retrieve the count of each resource tracked by Engine. + * This is intended for debugging. + * @{ + */ + size_t getBufferObjectCount() const noexcept; + size_t getViewCount() const noexcept; + size_t getSceneCount() const noexcept; + size_t getSwapChainCount() const noexcept; + size_t getStreamCount() const noexcept; + size_t getIndexBufferCount() const noexcept; + size_t getSkinningBufferCount() const noexcept; + size_t getMorphTargetBufferCount() const noexcept; + size_t getInstanceBufferCount() const noexcept; + size_t getVertexBufferCount() const noexcept; + size_t getIndirectLightCount() const noexcept; + size_t getMaterialCount() const noexcept; + size_t getTextureCount() const noexcept; + size_t getSkyboxeCount() const noexcept; + size_t getColorGradingCount() const noexcept; + size_t getRenderTargetCount() const noexcept; + /** @} */ + + /** + * This asynchronously executes user-defined commands. The commands are queued sequentially + * alongside other asynchronous operations (see Texture, VertexBuffer, and IndexBuffer) and + * guaranteed to be executed in the exact order they were invoked. + * + * Beware of overusing this method. It shares the execution queue with other asynchronous tasks + * like texture updates, so flooding it can delay those critical engine tasks. The recommended + * practice is to use this method for resource preparation, such as asset loading(images/meshes). + * This facilitates an efficient chaining pattern, where subsequent asynchronous operations + * (e.g., creating textures/vertex buffers) can be initiated directly within the completion + * callback. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param command The custom command to be executed. + * @param handler The handler from which `onComplete` is invoked. If null, it's called from the + * main thread. + * @param onComplete The callback function that runs once the command has finished. + * @param user The custom data that will be passed as an argument to the `onComplete`. + * @return A unique identifier for the asynchronous call. + */ + AsyncCallId runCommandAsync(utils::Invocable&& command, + backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback onComplete, + void* UTILS_NULLABLE user = nullptr); + + /** + * Cancel the pending asynchronous call pointed to by `id`, which is retrieved whenever you + * invoke a non-blocking version of method on an object, such as `Texture::setImageAsync` or + * `BufferObject::setBufferAsync`. + * + * @param id The unique identifier for the asynchronous call to be canceled. + * @return Returns true upon successful cancellation. It returns false if the asynchronous + * operation cannot be canceled because it is currently running, has finished, or has previously + * been canceled. + */ + bool cancelAsyncCall(AsyncCallId id); /** * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until @@ -831,6 +1083,25 @@ class UTILS_PUBLIC Engine { */ void flushAndWait(); + /** + * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until + * all commands to this point are executed. Note that does guarantee that the + * hardware is actually finished. + * + * A timeout can be specified, if for some reason this flushAndWait doesn't complete before the timeout, it will + * return false, true otherwise. + * + *

This is typically used right after destroying the SwapChain, + * in cases where a guarantee about the SwapChain destruction is needed in a + * timely fashion, such as when responding to Android's + * android.view.SurfaceHolder.Callback.surfaceDestroyed

+ * + * @param timeout A timeout in nanoseconds + * @return true if successful, false if flushAndWait timed out, in which case it wasn't successful and commands + * might still be executing on both the CPU and GPU sides. + */ + bool flushAndWait(uint64_t timeout); + /** * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) but does not wait * for commands to be either executed or the hardware finished. @@ -840,6 +1111,15 @@ class UTILS_PUBLIC Engine { */ void flush(); + /** + * Get paused state of rendering thread. + * + *

Warning: This is an experimental API. + * + * @see setPaused + */ + bool isPaused() const noexcept(UTILS_HAS_THREADING); + /** * Pause or resume rendering thread. * @@ -865,6 +1145,14 @@ class UTILS_PUBLIC Engine { */ void pumpMessageQueues(); + /** + * Switch the command queue to unprotected mode. Protected mode can be activated via + * Renderer::beginFrame() using a protected SwapChain. + * @see Renderer + * @see SwapChain + */ + void unprotected() noexcept; + /** * Returns the default Material. * @@ -948,8 +1236,54 @@ class UTILS_PUBLIC Engine { void resetBackendState() noexcept; #endif + /** + * Get the current time. This is a convenience function that simply returns the + * time in nanosecond since epoch of std::chrono::steady_clock. + * A possible implementation is: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * return std::chrono::steady_clock::now().time_since_epoch().count(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @return current time in nanosecond since epoch of std::chrono::steady_clock. + * @see Renderer::beginFrame() + */ + static uint64_t getSteadyClockTimeNano() noexcept; + + DebugRegistry& getDebugRegistry() noexcept; + /** + * Check if a feature flag exists + * @param name name of the feature flag to check + * @return true if the feature flag exists, false otherwise + */ + inline bool hasFeatureFlag(char const* UTILS_NONNULL name) noexcept { + return getFeatureFlag(name).has_value(); + } + + /** + * Set the value of a non-constant feature flag. + * @param name name of the feature flag to set + * @param value value to set + * @return true if the value was set, false if the feature flag is constant or doesn't exist. + */ + bool setFeatureFlag(char const* UTILS_NONNULL name, bool value) noexcept; + + /** + * Retrieves the value of any feature flag. + * @param name name of the feature flag + * @return the value of the flag if it exists + */ + std::optional getFeatureFlag(char const* UTILS_NONNULL name) const noexcept; + + /** + * Returns a pointer to a non-constant feature flag value. + * @param name name of the feature flag + * @return a pointer to the feature flag value, or nullptr if the feature flag is constant or doesn't exist + */ + bool* UTILS_NULLABLE getFeatureFlagPtr(char const* UTILS_NONNULL name) const noexcept; + protected: //! \privatesection Engine() noexcept = default; diff --git a/package/ios/libs/filament/include/filament/FilamentAPI.h b/package/ios/libs/filament/include/filament/FilamentAPI.h index 19d6ba24..d59e051d 100644 --- a/package/ios/libs/filament/include/filament/FilamentAPI.h +++ b/package/ios/libs/filament/include/filament/FilamentAPI.h @@ -19,6 +19,8 @@ #include #include +#include +#include #include @@ -54,6 +56,37 @@ class UTILS_PUBLIC FilamentAPI { template using BuilderBase = utils::PrivateImplementation; +// This needs to be public because it is used in the following template. +UTILS_PUBLIC void builderMakeName(utils::ImmutableCString& outName, const char* name, size_t len) noexcept; + +template +class UTILS_PUBLIC BuilderNameMixin { +public: + UTILS_DEPRECATED + Builder& name(const char* name, size_t len) noexcept { + builderMakeName(mName, name, len); + return static_cast(*this); + } + + Builder& name(utils::StaticString const& name) noexcept { + builderMakeName(mName, name.data(), name.size()); + return static_cast(*this); + } + + utils::ImmutableCString const& getName() const noexcept { return mName; } + + utils::ImmutableCString const& getNameOrDefault() const noexcept { + if (const auto& name = getName(); !name.empty()) { + return name; + } + static const utils::ImmutableCString sDefaultName = "(none)"; + return sDefaultName; + } + +private: + utils::ImmutableCString mName; +}; + } // namespace filament #endif // TNT_FILAMENT_FILAMENTAPI_H diff --git a/package/ios/libs/filament/include/filament/IndexBuffer.h b/package/ios/libs/filament/include/filament/IndexBuffer.h index 35b8a10e..9ac7dd3f 100644 --- a/package/ios/libs/filament/include/filament/IndexBuffer.h +++ b/package/ios/libs/filament/include/filament/IndexBuffer.h @@ -26,7 +26,9 @@ #include #include +#include +#include #include #include @@ -50,6 +52,9 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { public: using BufferDescriptor = backend::BufferDescriptor; + using AsyncCompletionCallback = + std::function; + using AsyncCallId = backend::AsyncCallId; /** * Type of the index buffer @@ -59,7 +64,7 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices }; - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -83,6 +88,56 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { */ Builder& bufferType(IndexType indexType) noexcept; + /** + * Associate an optional name with this IndexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this IndexBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this IndexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this IndexBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + + /** + * Specifies a callback that will execute once the resource's data has been fully allocated + * within the GPU memory. This enables the resource creation process to be handled + * asynchronously. + * + * Any asynchronous calls made during a resource's asynchronous creation (using this method) + * are safe because they are queued and executed in sequence. However, invoking regular + * methods on the same resource before it's fully ready is unsafe and may cause undefined + * behavior. Users can call the `isCreationComplete()` method for the resource to confirm + * when the resource is ready for regular API calls. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * @return This Builder, for chaining calls. + */ + Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback = nullptr, + void* UTILS_NULLABLE user = nullptr) noexcept; + /** * Creates the IndexBuffer object and returns a pointer to it. After creation, the index * buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer. @@ -103,22 +158,59 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI { }; /** - * Asynchronously copy-initializes a region of this IndexBuffer from the data provided. + * Copy-initializes a region of this IndexBuffer from the data provided. * * @param engine Reference to the filament::Engine to associate this IndexBuffer with. * @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer. * BufferDescriptor points to raw, untyped data that will be interpreted as * either 16-bit or 32-bits indices based on the Type of this IndexBuffer. - * @param byteOffset Offset in *bytes* into the IndexBuffer + * @param byteOffset Offset in *bytes* into the IndexBuffer. Must be multiple of 4. */ void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + /** + * An asynchronous version of `setBuffer()`. + * Copy-initializes a region of this IndexBuffer from the data provided. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param engine Reference to the filament::Engine to associate this IndexBuffer with. + * @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer. + * BufferDescriptor points to raw, untyped data that will be interpreted as + * either 16-bit or 32-bits indices based on the Type of this IndexBuffer. + * @param byteOffset Offset in *bytes* into the IndexBuffer. Must be multiple of 4. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + */ + AsyncCallId setBufferAsync(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset, + backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr); + /** * Returns the size of this IndexBuffer in elements. * @return The number of indices the IndexBuffer holds. */ size_t getIndexCount() const noexcept; + /** + * This non-blocking method checks if the resource has finished creation. If the resource + * creation was initiated asynchronously, it will return true only after all related + * asynchronous tasks are complete. If the resource was created normally without using async + * method, it will always return true. + * + * @return Whether the resource is created. + * + * @see Builder::async() + */ + bool isCreationComplete() const noexcept; + protected: // prevent heap allocation ~IndexBuffer() = default; diff --git a/package/ios/libs/filament/include/filament/IndirectLight.h b/package/ios/libs/filament/include/filament/IndirectLight.h index c230dac8..f5c2cfbe 100644 --- a/package/ios/libs/filament/include/filament/IndirectLight.h +++ b/package/ios/libs/filament/include/filament/IndirectLight.h @@ -158,6 +158,8 @@ class UTILS_PUBLIC IndirectLight : public FilamentAPI { * * @return This Builder, for chaining calls. * + * @see Material::Builder::sphericalHarmonicsBandCount() + * * @note * Because the coefficients are pre-scaled, `sh[0]` is the environment's * average irradiance. @@ -337,13 +339,24 @@ class UTILS_PUBLIC IndirectLight : public FilamentAPI { static math::float4 getColorEstimate(const math::float3 sh[UTILS_NONNULL 9], math::float3 direction) noexcept; - - /** @deprecated use static versions instead */ - UTILS_DEPRECATED + /** + * Helper to estimate the direction of the dominant light in the environment represented by + * spherical harmonics. + * Spherical harmonics must be set in the Builder or the result is undefined. + * @see getDirectionEstimate(const math::float3) + * @see Builder::irradiance(uint8_t, math::float3 const*) + * @see Builder::radiance(uint8_t, math::float3 const*) + */ math::float3 getDirectionEstimate() const noexcept; - /** @deprecated use static versions instead */ - UTILS_DEPRECATED + /** + * Helper to estimate the color and relative intensity of the environment represented by + * spherical harmonics in a given direction. + * Spherical harmonics must be set in the Builder or the result is undefined. + * @see getColorEstimate(const math::float3, math::float3) + * @see Builder::irradiance(uint8_t, math::float3 const*) + * @see Builder::radiance(uint8_t, math::float3 const*) + */ math::float4 getColorEstimate(math::float3 direction) const noexcept; protected: diff --git a/package/ios/libs/filament/include/filament/InstanceBuffer.h b/package/ios/libs/filament/include/filament/InstanceBuffer.h index 2135152d..8e7f788c 100644 --- a/package/ios/libs/filament/include/filament/InstanceBuffer.h +++ b/package/ios/libs/filament/include/filament/InstanceBuffer.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -38,13 +39,13 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI { struct BuilderDetails; public: - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: /** - * @param instanceCount the number of instances this InstanceBuffer will support, must be + * @param instanceCount The number of instances this InstanceBuffer will support, must be * >= 1 and <= \c Engine::getMaxAutomaticInstances() * @see Engine::getMaxAutomaticInstances */ @@ -70,10 +71,37 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI { */ Builder& localTransforms(math::mat4f const* UTILS_NULLABLE localTransforms) noexcept; + /** + * Associate an optional name with this InstanceBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this InstanceBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this InstanceBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this InstanceBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the InstanceBuffer object and returns a pointer to it. */ - InstanceBuffer* UTILS_NONNULL build(Engine& engine); + InstanceBuffer* UTILS_NONNULL build(Engine& engine) const; private: friend class FInstanceBuffer; @@ -96,6 +124,13 @@ class UTILS_PUBLIC InstanceBuffer : public FilamentAPI { void setLocalTransforms(math::mat4f const* UTILS_NONNULL localTransforms, size_t count, size_t offset = 0); + /** + * Returns the local transform for a given instance. + * @param index The index of the instance. + * @return The local transform of the instance. + */ + math::mat4f const& getLocalTransform(size_t index); + protected: // prevent heap allocation ~InstanceBuffer() = default; diff --git a/package/ios/libs/filament/include/filament/Material.h b/package/ios/libs/filament/include/filament/Material.h index b4b9bbed..3625bf6e 100644 --- a/package/ios/libs/filament/include/filament/Material.h +++ b/package/ios/libs/filament/include/filament/Material.h @@ -69,6 +69,20 @@ class UTILS_PUBLIC Material : public FilamentAPI { using ShaderModel = backend::ShaderModel; using SubpassType = backend::SubpassType; + /** + * Defines whether a material instance should use UBO batching or not. + */ + enum class UboBatchingMode { + /** + * For default, it follows the engine settings. + * If UBO batching is enabled on the engine and the material domain is SURFACE, it + * turns on the UBO batching. Otherwise, it turns off the UBO batching. + */ + DEFAULT, + //! Disable the Ubo Batching for this material + DISABLED, + }; + /** * Holds information about a material parameter. */ @@ -103,6 +117,11 @@ class UTILS_PUBLIC Material : public FilamentAPI { Builder& operator=(Builder const& rhs) noexcept; Builder& operator=(Builder&& rhs) noexcept; + enum class ShadowSamplingQuality : uint8_t { + HARD, // 2x2 PCF + LOW // 3x3 gaussian filter + }; + /** * Specifies the material data. The material data is a binary blob produced by * libfilamat or by matc. @@ -113,10 +132,10 @@ class UTILS_PUBLIC Material : public FilamentAPI { Builder& package(const void* UTILS_NONNULL payload, size_t size); template - using is_supported_constant_parameter_t = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value>::type; + using is_supported_constant_parameter_t = std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v>; /** * Specialize a constant parameter specified in the material definition with a concrete @@ -140,6 +159,33 @@ class UTILS_PUBLIC Material : public FilamentAPI { return constant(name, strlen(name), value); } + /** + * Sets the quality of the indirect lights computations. This is only taken into account + * if this material is lit and in the surface domain. This setting will affect the + * IndirectLight computation if one is specified on the Scene and Spherical Harmonics + * are used for the irradiance. + * + * @param shBandCount Number of spherical harmonic bands. Must be 1, 2 or 3 (default). + * @return Reference to this Builder for chaining calls. + * @see IndirectLight + */ + Builder& sphericalHarmonicsBandCount(size_t shBandCount) noexcept; + + /** + * Set the quality of shadow sampling. This is only taken into account + * if this material is lit and in the surface domain. + * @param quality + * @return + */ + Builder& shadowSamplingQuality(ShadowSamplingQuality quality) noexcept; + + /** + * Set the batching mode of the instances created from this material. + * @param uboBatchingMode + * @return + */ + Builder& uboBatching(UboBatchingMode uboBatchingMode) noexcept; + /** * Creates the Material object and returns a pointer to it. * @@ -152,7 +198,7 @@ class UTILS_PUBLIC Material : public FilamentAPI { * memory or other resources. * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. */ - Material* UTILS_NULLABLE build(Engine& engine); + Material* UTILS_NULLABLE build(Engine& engine) const; private: friend class FMaterial; }; @@ -325,6 +371,24 @@ class UTILS_PUBLIC Material : public FilamentAPI { //! Indicates whether an existing parameter is a sampler or not. bool isSampler(const char* UTILS_NONNULL name) const noexcept; + /** + * Returns a view of the material source (.mat which is a JSON-ish file) string, + * if it has been set. Otherwise, it returns a view of an empty string. + * The lifetime of the string_view is tied to the lifetime of the Material. + */ + std::string_view getSource() const noexcept; + /** + * + * Gets the name of the transform field associated for the given sampler parameter. + * In the case where the parameter does not have a transform name field, it will return nullptr. + * + * @param samplerName the name of the sampler parameter to query. + * + * @return If exists, the transform name value otherwise returns a nullptr. + */ + const char* UTILS_NULLABLE getParameterTransformName( + const char* UTILS_NONNULL samplerName) const noexcept; + /** * Sets the value of the given parameter on this material's default instance. * diff --git a/package/ios/libs/filament/include/filament/MaterialChunkType.h b/package/ios/libs/filament/include/filament/MaterialChunkType.h index c80ac7d8..e321b386 100644 --- a/package/ios/libs/filament/include/filament/MaterialChunkType.h +++ b/package/ios/libs/filament/include/filament/MaterialChunkType.h @@ -45,17 +45,20 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialEssl1 = charTo64bitNum("MAT_ESS1"), MaterialSpirv = charTo64bitNum("MAT_SPIR"), MaterialMetal = charTo64bitNum("MAT_METL"), + MaterialWgsl = charTo64bitNum("MAT_WGSL"), MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"), MaterialShaderModels = charTo64bitNum("MAT_SMDL"), - MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), - MaterialUniformBindings = charTo64bitNum("MAT_UNIF"), MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"), MaterialAttributeInfo = charTo64bitNum("MAT_ATTR"), + MaterialDescriptorBindingsInfo = charTo64bitNum("MAT_DBDI"), + MaterialDescriptorSetLayoutInfo = charTo64bitNum("MAT_DSLI"), MaterialProperties = charTo64bitNum("MAT_PROP"), MaterialConstants = charTo64bitNum("MAT_CONS"), + MaterialPushConstants = charTo64bitNum("MAT_PCON"), MaterialName = charTo64bitNum("MAT_NAME"), MaterialVersion = charTo64bitNum("MAT_VERS"), + MaterialCompilationParameters = charTo64bitNum("MAT_CPRM"), MaterialCacheId = charTo64bitNum("MAT_UUID"), MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"), MaterialShading = charTo64bitNum("MAT_SHAD"), @@ -91,10 +94,15 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialVertexDomain = charTo64bitNum("MAT_VEDO"), MaterialInterpolation = charTo64bitNum("MAT_INTR"), + MaterialStereoscopicType = charTo64bitNum("MAT_STER"), DictionaryText = charTo64bitNum("DIC_TEXT"), DictionarySpirv = charTo64bitNum("DIC_SPIR"), DictionaryMetalLibrary = charTo64bitNum("DIC_MLIB"), + + MaterialCrc32 = charTo64bitNum("MAT_CRC "), + + MaterialSource = charTo64bitNum("MAT_SRC "), }; } // namespace filamat diff --git a/package/ios/libs/filament/include/filament/MaterialEnums.h b/package/ios/libs/filament/include/filament/MaterialEnums.h index 9f348481..fb2bea79 100644 --- a/package/ios/libs/filament/include/filament/MaterialEnums.h +++ b/package/ios/libs/filament/include/filament/MaterialEnums.h @@ -28,7 +28,16 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 51; +static constexpr size_t MATERIAL_VERSION = 68; + +// Those are the api levels that are used in the source material file (.mat) +// +// The level used for the apis that are already public and stable. Any released api supports +// backward compatibility (i.e. No breaking changes will be introduced for those apis.) +static constexpr uint32_t RELEASED_MATERIAL_API_LEVEL = 1; +// The level used for the apis that are currently under development or development has completed +// but may introduce breaking changes. +static constexpr uint32_t UNSTABLE_MATERIAL_API_LEVEL = 2; /** * Supported shading models @@ -203,7 +212,7 @@ enum class ReflectionMode : uint8_t { // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; -static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27; +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 31; enum class Property : uint8_t { BASE_COLOR, //!< float4, all shading models ROUGHNESS, //!< float, lit shading models only @@ -230,8 +239,12 @@ enum class Property : uint8_t { ABSORPTION, //!< float3, how much light is absorbed by the material TRANSMISSION, //!< float, how much light is refracted through the material IOR, //!< float, material's index of refraction + DISPERSION, //!< float, material's dispersion MICRO_THICKNESS, //!< float, thickness of the thin layer BENT_NORMAL, //!< float3, all shading models only, except unlit + SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth + SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth + SHADOW_STRENGTH, //!< float, [0, 1] strength of shadows received by this material // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT }; @@ -255,4 +268,7 @@ enum class UserVariantFilterBit : UserVariantFilterMask { template<> struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableIntegerOperators + : public std::true_type {}; + #endif diff --git a/package/ios/libs/filament/include/filament/MaterialInstance.h b/package/ios/libs/filament/include/filament/MaterialInstance.h index 7252f9a4..b052c8f7 100644 --- a/package/ios/libs/filament/include/filament/MaterialInstance.h +++ b/package/ios/libs/filament/include/filament/MaterialInstance.h @@ -19,7 +19,7 @@ #include #include - +#include #include #include @@ -56,35 +56,36 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { }; public: - using CullingMode = filament::backend::CullingMode; + using CullingMode = backend::CullingMode; + // ReSharper disable once CppRedundantQualifier using TransparencyMode = filament::TransparencyMode; - using DepthFunc = filament::backend::SamplerCompareFunc; - using StencilCompareFunc = filament::backend::SamplerCompareFunc; - using StencilOperation = filament::backend::StencilOperation; - using StencilFace = filament::backend::StencilFace; + using DepthFunc = backend::SamplerCompareFunc; + using StencilCompareFunc = backend::SamplerCompareFunc; + using StencilOperation = backend::StencilOperation; + using StencilFace = backend::StencilFace; template - using is_supported_parameter_t = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || + using is_supported_parameter_t = std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || // these types are slower as they need a layout conversion - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value - >::type; + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v + >; /** * Creates a new MaterialInstance using another MaterialInstance as a template for initialization. @@ -121,13 +122,13 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { /** inline helper to provide the name as a null-terminated string literal */ template> - inline void setParameter(StringLiteral name, T const& value) { + void setParameter(StringLiteral const name, T const& value) { setParameter(name.data, name.size, value); } /** inline helper to provide the name as a null-terminated C string */ template> - inline void setParameter(const char* UTILS_NONNULL name, T const& value) { + void setParameter(const char* UTILS_NONNULL name, T const& value) { setParameter(name, strlen(name), value); } @@ -140,6 +141,7 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { * @param values Array of values to set to the named parameter array. * @param count Size of the array to set. * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + * @see Material::hasParameter */ template> void setParameter(const char* UTILS_NONNULL name, size_t nameLength, @@ -147,14 +149,14 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { /** inline helper to provide the name as a null-terminated string literal */ template> - inline void setParameter(StringLiteral name, const T* UTILS_NONNULL values, size_t count) { + void setParameter(StringLiteral const name, const T* UTILS_NONNULL values, size_t const count) { setParameter(name.data, name.size, values, count); } /** inline helper to provide the name as a null-terminated C string */ template> - inline void setParameter(const char* UTILS_NONNULL name, - const T* UTILS_NONNULL values, size_t count) { + void setParameter(const char* UTILS_NONNULL name, + const T* UTILS_NONNULL values, size_t const count) { setParameter(name, strlen(name), values, count); } @@ -175,14 +177,14 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler); /** inline helper to provide the name as a null-terminated string literal */ - inline void setParameter(StringLiteral name, - Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { + void setParameter(StringLiteral const name, + Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { setParameter(name.data, name.size, texture, sampler); } /** inline helper to provide the name as a null-terminated C string */ - inline void setParameter(const char* UTILS_NONNULL name, - Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { + void setParameter(const char* UTILS_NONNULL name, + Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) { setParameter(name, strlen(name), texture, sampler); } @@ -201,12 +203,12 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { RgbType type, math::float3 color); /** inline helper to provide the name as a null-terminated string literal */ - inline void setParameter(StringLiteral name, RgbType type, math::float3 color) { + void setParameter(StringLiteral const name, RgbType const type, math::float3 const color) { setParameter(name.data, name.size, type, color); } /** inline helper to provide the name as a null-terminated C string */ - inline void setParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) { + void setParameter(const char* UTILS_NONNULL name, RgbType const type, math::float3 const color) { setParameter(name, strlen(name), type, color); } @@ -225,34 +227,38 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { RgbaType type, math::float4 color); /** inline helper to provide the name as a null-terminated string literal */ - inline void setParameter(StringLiteral name, RgbaType type, math::float4 color) { + void setParameter(StringLiteral const name, RgbaType const type, math::float4 const color) { setParameter(name.data, name.size, type, color); } /** inline helper to provide the name as a null-terminated C string */ - inline void setParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) { + void setParameter(const char* UTILS_NONNULL name, RgbaType const type, math::float4 const color) { setParameter(name, strlen(name), type, color); } /** * Gets the value of a parameter by name. * + * Note: Only supports non-texture parameters such as numeric and math types. + * * @param name Name of the parameter as defined by Material. Cannot be nullptr. * @param nameLength Length in `char` of the name parameter. * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + * + * @see Material::hasParameter */ template T getParameter(const char* UTILS_NONNULL name, size_t nameLength) const; /** inline helper to provide the name as a null-terminated C string */ template> - inline T getParameter(StringLiteral name) const { + T getParameter(StringLiteral const name) const { return getParameter(name.data, name.size); } /** inline helper to provide the name as a null-terminated C string */ template> - inline T getParameter(const char* UTILS_NONNULL name) const { + T getParameter(const char* UTILS_NONNULL name) const { return getParameter(name, strlen(name)); } @@ -371,11 +377,22 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { */ void setCullingMode(CullingMode culling) noexcept; + /** + * Overrides the default triangle culling state that was set on the material separately for the + * color and shadow passes + */ + void setCullingMode(CullingMode colorPassCullingMode, CullingMode shadowPassCullingMode) noexcept; + /** * Returns the face culling mode. */ CullingMode getCullingMode() const noexcept; + /** + * Returns the face culling mode for the shadow passes. + */ + CullingMode getShadowCullingMode() const noexcept; + /** * Overrides the default color-buffer write state that was set on the material. */ @@ -513,6 +530,13 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { void setStencilWriteMask(uint8_t writeMask, StencilFace face = StencilFace::FRONT_AND_BACK) noexcept; + /** + * PostProcess and compute domain material instance must be commited manually. This call has + * no effect on surface domain materials. + * @param engine Filament engine + */ + void commit(Engine& engine) const; + protected: // prevent heap allocation ~MaterialInstance() = default; diff --git a/package/ios/libs/filament/include/filament/MorphTargetBuffer.h b/package/ios/libs/filament/include/filament/MorphTargetBuffer.h index 655bb8d8..594f1a6c 100644 --- a/package/ios/libs/filament/include/filament/MorphTargetBuffer.h +++ b/package/ios/libs/filament/include/filament/MorphTargetBuffer.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -39,7 +40,7 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI { struct BuilderDetails; public: - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -63,6 +64,33 @@ class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI { */ Builder& count(size_t count) noexcept; + /** + * Associate an optional name with this MorphTargetBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this MorphTargetBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this MorphTargetBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this MorphTargetBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the MorphTargetBuffer object and returns a pointer to it. * diff --git a/package/ios/libs/filament/include/filament/Options.h b/package/ios/libs/filament/include/filament/Options.h index 3966053e..422a0222 100644 --- a/package/ios/libs/filament/include/filament/Options.h +++ b/package/ios/libs/filament/include/filament/Options.h @@ -70,8 +70,8 @@ enum class BlendMode : uint8_t { * * \note * Dynamic resolution is only supported on platforms where the time to render - * a frame can be measured accurately. Dynamic resolution is currently only - * supported on Android. + * a frame can be measured accurately. On platforms where this is not supported, + * Dynamic Resolution can't be enabled unless minScale == maxScale. * * @see Renderer::FrameRateOptions * @@ -86,12 +86,15 @@ struct DynamicResolutionOptions { /** * Upscaling quality * LOW: bilinear filtered blit. Fastest, poor quality - * MEDIUM: AMD FidelityFX FSR1 w/ mobile optimizations + * MEDIUM: Qualcomm Snapdragon Game Super Resolution (SGSR) 1.0 * HIGH: AMD FidelityFX FSR1 w/ mobile optimizations * ULTRA: AMD FidelityFX FSR1 - * FSR1 require a well anti-aliased (MSAA or TAA), noise free scene. + * FSR1 and SGSR require a well anti-aliased (MSAA or TAA), noise free scene. + * Avoid FXAA and dithering. * * The default upscaling quality is set to LOW. + * + * caveat: currently, 'quality' is always set to LOW if the View is TRANSLUCENT. */ QualityLevel quality = QualityLevel::LOW; }; @@ -164,7 +167,9 @@ struct BloomOptions { }; /** - * Options to control large-scale fog in the scene + * Options to control large-scale fog in the scene. Materials can enable the `linearFog` property, + * which uses a simplified, linear equation for fog calculation; in this mode, the heightFalloff + * is ignored as well as the mipmap selection in IBL or skyColor mode. */ struct FogOptions { /** @@ -183,7 +188,7 @@ struct FogOptions { float cutOffDistance = INFINITY; /** - * fog's maximum opacity between 0 and 1 + * fog's maximum opacity between 0 and 1. Ignored in `linearFog` mode. */ float maximumOpacity = 1.0f; @@ -193,12 +198,15 @@ struct FogOptions { float height = 0.0f; /** - * How fast the fog dissipates with altitude. heightFalloff has a unit of [1/m]. + * How fast the fog dissipates with the altitude. heightFalloff has a unit of [1/m]. * It can be expressed as 1/H, where H is the altitude change in world units [m] that causes a * factor 2.78 (e) change in fog density. * * A falloff of 0 means the fog density is constant everywhere and may result is slightly * faster computations. + * + * In `linearFog` mode, only use to compute the slope of the linear equation. Completely + * ignored if set to 0. */ float heightFalloff = 1.0f; @@ -220,7 +228,7 @@ struct FogOptions { LinearColor color = { 1.0f, 1.0f, 1.0f }; /** - * Extinction factor in [1/m] at altitude 'height'. The extinction factor controls how much + * Extinction factor in [1/m] at an altitude 'height'. The extinction factor controls how much * light is absorbed and out-scattered per unit of distance. Each unit of extinction reduces * the incoming light to 37% of its original value. * @@ -229,11 +237,16 @@ struct FogOptions { * the composition of the fog/atmosphere. * * For historical reason this parameter is called `density`. + * + * In `linearFog` mode this is the slope of the linear equation if heightFalloff is set to 0. + * Otherwise, heightFalloff affects the slope calculation such that it matches the slope of + * the standard equation at the camera height. */ float density = 0.1f; /** * Distance in world units [m] from the camera where the Sun in-scattering starts. + * Ignored in `linearFog` mode. */ float inScatteringStart = 0.0f; @@ -242,6 +255,7 @@ struct FogOptions { * is scattered (by the fog) towards the camera. * Size of the Sun in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100). * Smaller values result is a larger scattering size. + * Ignored in `linearFog` mode. */ float inScatteringSize = -1.0f; @@ -269,6 +283,8 @@ struct FogOptions { * * `fogColorFromIbl` is ignored when skyTexture is specified. * + * In `linearFog` mode mipmap level 0 is always used. + * * @see Texture * @see fogColorFromIbl */ @@ -283,7 +299,7 @@ struct FogOptions { /** * Options to control Depth of Field (DoF) effect in the scene. * - * cocScale can be used to set the depth of field blur independently from the camera + * cocScale can be used to set the depth of field blur independently of the camera * aperture, e.g. for artistic reasons. This can be achieved by setting: * cocScale = cameraAperture / desiredDoFAperture * @@ -373,18 +389,30 @@ struct RenderQuality { * @see setAmbientOcclusionOptions() */ struct AmbientOcclusionOptions { + enum class AmbientOcclusionType : uint8_t { + SAO, //!< use Scalable Ambient Occlusion + GTAO, //!< use Ground Truth-Based Ambient Occlusion + }; + + AmbientOcclusionType aoType = AmbientOcclusionType::SAO;//!< Type of ambient occlusion algorithm. float radius = 0.3f; //!< Ambient Occlusion radius in meters, between 0 and ~10. float power = 1.0f; //!< Controls ambient occlusion's contrast. Must be positive. - float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. + + /** + * Self-occlusion bias in meters. Use to avoid self-occlusion. + * Between 0 and a few mm. No effect when aoType set to GTAO + */ + float bias = 0.0005f; + float resolution = 0.5f;//!< How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0. float intensity = 1.0f; //!< Strength of the Ambient Occlusion effect. float bilateralThreshold = 0.05f; //!< depth distance that constitute an edge for filtering - QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO. - QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness + QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO and params for filtering + QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO. QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality bool enabled = false; //!< enables or disables screen-space ambient occlusion bool bentNormals = false; //!< enables bent normals computation from AO, and specular AO - float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider + float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider. No effect when aoType set to GTAO. /** * Screen Space Cone Tracing (SSCT) options * Ambient shadows from dominant light @@ -402,6 +430,29 @@ struct AmbientOcclusionOptions { bool enabled = false; //!< enables or disables SSCT }; Ssct ssct; // %codegen_skip_javascript% %codegen_java_flatten% + + /** + * Ground Truth-base Ambient Occlusion (GTAO) options + */ + struct Gtao { + uint8_t sampleSliceCount = 4; //!< # of slices. Higher value makes less noise. + uint8_t sampleStepsPerSlice = 3; //!< # of steps the radius is divided into for integration. Higher value makes less bias. + float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0. No effect when useVisibilityBitmasks sets to true. + + /** + * Enables or disables visibility bitmasks mode. Notes that bent normal doesn't work under this mode. + * Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation. + */ + bool useVisibilityBitmasks = false; + float constThickness = 0.5f; //!< constant thickness value of objects on the screen in world space. Only take effect when useVisibilityBitmasks is set to true. + + /** + * Increase thickness with distance to maintain detail on distant surfaces. + * Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation. + */ + bool linearThickness = false; + }; + Gtao gtao; // %codegen_skip_javascript% %codegen_java_flatten% }; /** @@ -438,16 +489,15 @@ struct MultiSampleAntiAliasingOptions { * @see setTemporalAntiAliasingOptions() */ struct TemporalAntiAliasingOptions { - float filterWidth = 1.0f; //!< reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother) + float filterWidth = 1.0f; //!< @deprecated has no effect. float feedback = 0.12f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA). float lodBias = -1.0f; //!< texturing lod bias (typically -1 or -2) float sharpness = 0.0f; //!< post-TAA sharpen, especially useful when upscaling is true. bool enabled = false; //!< enables or disables temporal anti-aliasing - bool upscaling = false; //!< 4x TAA upscaling. Disables Dynamic Resolution. [BETA] + float upscaling = 1.0f; //!< Upscaling factor. Disables Dynamic Resolution. [BETA] enum class BoxType : uint8_t { AABB, //!< use an AABB neighborhood - VARIANCE, //!< use the variance of the neighborhood (not recommended) AABB_VARIANCE //!< use both AABB and variance }; @@ -468,6 +518,7 @@ struct TemporalAntiAliasingOptions { bool filterHistory = true; //!< whether to filter the history buffer bool filterInput = true; //!< whether to apply the reconstruction filter to the input bool useYCoCg = false; //!< whether to use the YcoCg color-space for history rejection + bool hdr = true; //!< set to true for HDR content BoxType boxType = BoxType::AABB; //!< type of color gamut box BoxClipping boxClipping = BoxClipping::ACCURATE; //!< clipping algorithm JitterPattern jitterPattern = JitterPattern::HALTON_23_X16; //! Jitter Pattern diff --git a/package/ios/libs/filament/include/filament/RenderTarget.h b/package/ios/libs/filament/include/filament/RenderTarget.h index fc76111d..02eb6714 100644 --- a/package/ios/libs/filament/include/filament/RenderTarget.h +++ b/package/ios/libs/filament/include/filament/RenderTarget.h @@ -81,7 +81,7 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { }; //! Use Builder to construct a RenderTarget object instance - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -118,7 +118,7 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { Builder& mipLevel(AttachmentPoint attachment, uint8_t level) noexcept; /** - * Sets the cubemap face for a given attachment point. + * Sets the face for cubemap textures at the given attachment point. * * @param attachment The attachment point. * @param face The associated cubemap face. @@ -127,7 +127,12 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { Builder& face(AttachmentPoint attachment, CubemapFace face) noexcept; /** - * Sets the layer for a given attachment point (for 3D textures). + * Sets an index of a single layer for 2d array, cubemap array, and 3d textures at the given + * attachment point. + * + * For cubemap array textures, layer is translated into an array index and face according to + * - index: layer / 6 + * - face: layer % 6 * * @param attachment The attachment point. * @param layer The associated cubemap layer. @@ -135,6 +140,27 @@ class UTILS_PUBLIC RenderTarget : public FilamentAPI { */ Builder& layer(AttachmentPoint attachment, uint32_t layer) noexcept; + /** + * Sets the starting index of the 2d array textures for multiview at the given attachment + * point. + * + * This requires COLOR and DEPTH attachments (if set) to be of 2D array textures. + * + * @param attachment The attachment point. + * @param layerCount The number of layers used for multiview, starting from baseLayer. + * @param baseLayer The starting index of the 2d array texture. + * @return A reference to this Builder for chaining calls. + */ + Builder& multiview(AttachmentPoint attachment, uint8_t layerCount, uint8_t baseLayer = 0) noexcept; + + /** + * Sets the number of samples used for MSAA (Multisample Anti-Aliasing). + * + * @param samples The number of samples used for multisampling. + * @return A reference to this Builder for chaining calls. + */ + Builder& samples(uint8_t samples) noexcept; + /** * Creates the RenderTarget object and returns a pointer to it. * diff --git a/package/ios/libs/filament/include/filament/RenderableManager.h b/package/ios/libs/filament/include/filament/RenderableManager.h index bb50b7d1..1428ed1e 100644 --- a/package/ios/libs/filament/include/filament/RenderableManager.h +++ b/package/ios/libs/filament/include/filament/RenderableManager.h @@ -220,7 +220,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * the renderable are immutable. * STATIC geometry has the same restrictions as STATIC_BOUNDS, but in addition disallows * skinning, morphing and changing the VertexBuffer or IndexBuffer in any way. - * @param enable whether this renderable has static bounds. false by default. + * @param type type of geometry. */ Builder& geometryType(GeometryType type) noexcept; @@ -297,7 +297,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& priority(uint8_t priority) noexcept; /** - * Set the channel this renderable is associated to. There can be 4 channels. + * Set the channel this renderable is associated to. There can be 8 channels. * All renderables in a given channel are rendered together, regardless of anything else. * They are sorted as usual within a channel. * Channels work similarly to priorities, except that they enforce the strongest ordering. @@ -305,7 +305,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * Channels 0 and 1 may not have render primitives using a material with `refractionType` * set to `screenspace`. * - * @param channel clamped to the range [0..3], defaults to 2. + * @param channel clamped to the range [0..7], defaults to 2. * * @return Builder reference for chaining calls. * @@ -454,7 +454,7 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * * @param primitiveIndex zero-based index of the primitive, must be less than the primitive * count passed to Builder constructor - * @param indicesAndWeightsVectors pairs of bone index and bone weight for all vertices of + * @param indicesAndWeightsVector pairs of bone index and bone weight for all vertices of * the primitive sequentially * * @return Builder reference for chaining calls. @@ -464,16 +464,11 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& boneIndicesAndWeights(size_t primitiveIndex, utils::FixedCapacityVector< utils::FixedCapacityVector> indicesAndWeightsVector) noexcept; + /** - * Controls if the renderable has vertex morphing targets, zero by default. This is + * Controls if the renderable has legacy vertex morphing targets, zero by default. This is * required to enable GPU morphing. * - * Filament supports two morphing modes: standard (default) and legacy. - * - * For standard morphing, A MorphTargetBuffer must be created and provided via - * RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to - * \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. - * * For legacy morphing, the attached VertexBuffer must provide data in the * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must @@ -486,26 +481,29 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { Builder& morphing(size_t targetCount) noexcept; /** - * Specifies the morph target buffer for a primitive. + * Controls if the renderable has vertex morphing targets, zero by default. This is + * required to enable GPU morphing. * - * The morph target buffer must have an associated renderable and geometry. Two conditions - * must be met: - * 1. The number of morph targets in the buffer must equal the renderable's morph target - * count. - * 2. The vertex count of each morph target must equal the geometry's vertex count. + * Filament supports two morphing modes: standard (default) and legacy. + * + * For standard morphing, A MorphTargetBuffer must be provided. + * Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. + * + * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis + * to advance the animation. + */ + Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; + + /** + * Specifies the the range of the MorphTargetBuffer to use with this primitive. * * @param level the level of detail (lod), only 0 can be specified * @param primitiveIndex zero-based index of the primitive, must be less than the count passed to Builder constructor - * @param morphTargetBuffer specifies the morph target buffer * @param offset specifies where in the morph target buffer to start reading (expressed as a number of vertices) - * @param count number of vertices in the morph target buffer to read, must equal the geometry's count (for triangles, this should be a multiple of 3) */ - Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, - size_t offset, size_t count) noexcept; + Builder& morphing(uint8_t level, + size_t primitiveIndex, size_t offset) noexcept; - inline Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; /** * Sets the drawing order for blended primitives. The drawing order is either global or @@ -599,21 +597,6 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { friend class FEngine; friend class FRenderPrimitive; friend class FRenderableManager; - struct Entry { - VertexBuffer* UTILS_NULLABLE vertices = nullptr; - IndexBuffer* UTILS_NULLABLE indices = nullptr; - size_t offset = 0; - size_t count = 0; - MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr; - PrimitiveType type = PrimitiveType::TRIANGLES; - uint16_t blendOrder = 0; - bool globalBlendOrderEnabled = false; - struct { - MorphTargetBuffer* UTILS_NULLABLE buffer = nullptr; - size_t offset = 0; - size_t count = 0; - } morphing; - }; }; /** @@ -765,20 +748,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /** * Associates a MorphTargetBuffer to the given primitive. */ - void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count); - - /** - * Utility method to change a MorphTargetBuffer to the given primitive - */ - inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer); + void setMorphTargetBufferOffsetAt(Instance instance, uint8_t level, size_t primitiveIndex, + size_t offset); /** - * Get a MorphTargetBuffer to the given primitive or null if it doesn't exist. + * Get a MorphTargetBuffer to the given renderable or null if it doesn't exist. */ - MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance, - uint8_t level, size_t primitiveIndex) const noexcept; + MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBuffer(Instance instance) const noexcept; /** * Gets the number of morphing in the given entity. @@ -807,6 +783,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { */ size_t getPrimitiveCount(Instance instance) const noexcept; + /** + * Returns the number of instances for this renderable. + * @param instance Instance of the component obtained from getInstance(). + * @return The number of instances. + */ + size_t getInstanceCount(Instance instance) const noexcept; + /** * Changes the material instance binding for the given primitive. * @@ -822,6 +805,13 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { void setMaterialInstanceAt(Instance instance, size_t primitiveIndex, MaterialInstance const* UTILS_NONNULL materialInstance); + /** + * Clear the MaterialInstance for the given primitive. + * @param instance Renderable's instance + * @param primitiveIndex Primitive index + */ + void clearMaterialInstanceAt(Instance instance, size_t primitiveIndex); + /** * Retrieves the material instance that is bound to the given primitive. */ @@ -869,20 +859,20 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /*! \cond PRIVATE */ template struct is_supported_vector_type { - using type = typename std::enable_if< - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value - >::type; + using type = std::enable_if_t< + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v + >; }; template struct is_supported_index_type { - using type = typename std::enable_if< - std::is_same::value || - std::is_same::value - >::type; + using type = std::enable_if_t< + std::is_same_v || + std::is_same_v + >; }; /*! \endcond */ @@ -906,20 +896,6 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { ~RenderableManager() = default; }; -RenderableManager::Builder& RenderableManager::Builder::morphing( - uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept { - return morphing(level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); -} - -void RenderableManager::setMorphTargetBufferAt( - Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) { - setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); -} - template Box RenderableManager::computeAABB( VECTOR const* UTILS_NONNULL vertices, diff --git a/package/ios/libs/filament/include/filament/Renderer.h b/package/ios/libs/filament/include/filament/Renderer.h index fdc291b1..53c13cab 100644 --- a/package/ios/libs/filament/include/filament/Renderer.h +++ b/package/ios/libs/filament/include/filament/Renderer.h @@ -22,9 +22,11 @@ #include #include +#include #include +#include #include namespace filament { @@ -81,6 +83,50 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { UTILS_DEPRECATED uint64_t vsyncOffsetNanos = 0; }; + /** + * Timing information about a frame + * @see getFrameInfoHistory() + */ + struct FrameInfo { + /** duration in nanosecond since epoch of std::steady_clock */ + using time_point_ns = int64_t; + /** duration in nanosecond on the std::steady_clock */ + using duration_ns = int64_t; + static constexpr time_point_ns INVALID = -1; //!< value not supported + static constexpr time_point_ns PENDING = -2; //!< value not yet available + uint32_t frameId; //!< monotonically increasing frame identifier + duration_ns gpuFrameDuration; //!< frame duration on the GPU in nanosecond [ns] + duration_ns denoisedGpuFrameDuration; //!< denoised frame duration on the GPU in [ns] + time_point_ns beginFrame; //!< Renderer::beginFrame() time since epoch [ns] + time_point_ns endFrame; //!< Renderer::endFrame() time since epoch [ns] + time_point_ns backendBeginFrame; //!< Backend thread time of frame start since epoch [ns] + time_point_ns backendEndFrame; //!< Backend thread time of frame end since epoch [ns] + time_point_ns gpuFrameComplete; //!< GPU thread time of frame end since epoch [ns] or 0 + time_point_ns vsync; //!< VSYNC time of this frame since epoch [ns] + time_point_ns displayPresent; //!< Actual presentation time of this frame since epoch [ns] + time_point_ns presentDeadline; //!< deadline for queuing a frame [ns] + duration_ns displayPresentInterval; //!< display refresh rate [ns] + duration_ns compositionToPresentLatency; //!< time between the start of composition and the expected present time [ns] + time_point_ns expectedPresentTime; //!< system's expected presentation time since epoch [ns] + }; + + /** + * Retrieve a history of frame timing information. The maximum frame history size is + * given by getMaxFrameHistorySize(). + * All or part of the history can be lost when using a different SwapChain in beginFrame(). + * @param historySize requested history size. The returned vector could be smaller. + * @return A vector of FrameInfo. + * @see beginFrame() + */ + utils::FixedCapacityVector getFrameInfoHistory( + size_t historySize = 1) const noexcept; + + /** + * @return the maximum supported frame history size. + * @see getFrameInfoHistory() + */ + size_t getMaxFrameHistorySize() const noexcept; + /** * Use FrameRateOptions to set the desired frame rate and control how quickly the system * reacts to GPU load changes. @@ -228,9 +274,42 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { /** - * Set-up a frame for this Renderer. + * The use of this method is optional. It sets the VSYNC time expressed as the duration in + * nanosecond since epoch of std::chrono::steady_clock. + * If called, passing 0 to vsyncSteadyClockTimeNano in Renderer::BeginFrame will use this + * time instead. + * @param steadyClockTimeNano duration in nanosecond since epoch of std::chrono::steady_clock + * @see Engine::getSteadyClockTimeNano() + * @see Renderer::BeginFrame() + */ + void setVsyncTime(uint64_t steadyClockTimeNano) noexcept; + + /** + * Call skipFrame when momentarily skipping frames, for instance if the content of the + * scene doesn't change. + * + * @param vsyncSteadyClockTimeNano + */ + void skipFrame(uint64_t vsyncSteadyClockTimeNano = 0u); + + /** + * Returns true if the current frame should be rendered. + * + * This is a convenience method that returns the same value as beginFrame(). + * + * @return + * *false* the current frame should be skipped, + * *true* the current frame can be rendered + * + * @see + * beginFrame() + */ + bool shouldRenderFrame() const noexcept; + + /** + * Set up a frame for this Renderer. * - * beginFrame() manages frame pacing, and returns whether or not a frame should be drawn. The + * beginFrame() manages frame-pacing, and returns whether a frame should be drawn. The * goal of this is to skip frames when the GPU falls behind in order to keep the frame * latency low. * @@ -249,6 +328,8 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * or 0 if unknown. This value should be the timestamp of * the last h/w vsync. It is expressed in the * std::chrono::steady_clock time base. + * On Android this should be the frame time received from + * a Choreographer. * @param swapChain A pointer to the SwapChain instance to use. * * @return @@ -260,6 +341,8 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * * @note * All calls to render() must happen *after* beginFrame(). + * It is recommended to use the same swapChain for every call to beginFrame, failing to do + * so can result is losing all or part of the FrameInfo history. * * @see * endFrame() @@ -456,7 +539,7 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * * Framebuffer as seen on User buffer (PixelBufferDescriptor&) * screen - * + * * +--------------------+ * | | .stride .alignment * | | ----------------------->--> @@ -486,6 +569,9 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { * uploaded to it via setImage, the data returned from readPixels will be y-flipped with respect * to the setImage call. * + * Note: the texture that backs the COLOR attachment for `renderTarget` must have + * TextureUsage::BLIT_SRC as part of its usage. + * * @remark * readPixels() is intended for debugging and testing. It will impact performance significantly. * @@ -577,6 +663,19 @@ class UTILS_PUBLIC Renderer : public FilamentAPI { */ void resetUserTime(); + + /** + * Requests the next frameCount frames to be skipped. For Debugging. + * @param frameCount number of frames to skip. + */ + void skipNextFrames(size_t frameCount) const noexcept; + + /** + * Remainder count of frame to be skipped + * @return remaining frames to be skipped + */ + size_t getFrameToSkipCount() const noexcept; + protected: // prevent heap allocation ~Renderer() = default; diff --git a/package/ios/libs/filament/include/filament/Scene.h b/package/ios/libs/filament/include/filament/Scene.h index 9df6285c..8758ab70 100644 --- a/package/ios/libs/filament/include/filament/Scene.h +++ b/package/ios/libs/filament/include/filament/Scene.h @@ -141,6 +141,11 @@ class UTILS_PUBLIC Scene : public FilamentAPI { */ void removeEntities(const utils::Entity* UTILS_NONNULL entities, size_t count); + /** + * Remove all entities to the Scene. + */ + void removeAllEntities() noexcept; + /** * Returns the total number of Entities in the Scene, whether alive or not. * @return Total number of Entities in the Scene. diff --git a/package/ios/libs/filament/include/filament/SkinningBuffer.h b/package/ios/libs/filament/include/filament/SkinningBuffer.h index 36ae30ed..c397f62b 100644 --- a/package/ios/libs/filament/include/filament/SkinningBuffer.h +++ b/package/ios/libs/filament/include/filament/SkinningBuffer.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -39,7 +40,7 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI { struct BuilderDetails; public: - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -69,6 +70,33 @@ class UTILS_PUBLIC SkinningBuffer : public FilamentAPI { */ Builder& initialize(bool initialize = true) noexcept; + /** + * Associate an optional name with this SkinningBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this SkinningBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this SkinningBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this SkinningBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the SkinningBuffer object and returns a pointer to it. * diff --git a/package/ios/libs/filament/include/filament/Skybox.h b/package/ios/libs/filament/include/filament/Skybox.h index ce203aae..bacd0acb 100644 --- a/package/ios/libs/filament/include/filament/Skybox.h +++ b/package/ios/libs/filament/include/filament/Skybox.h @@ -131,6 +131,20 @@ class UTILS_PUBLIC Skybox : public FilamentAPI { */ Builder& color(math::float4 color) noexcept; + /** + * Set the rendering priority of the Skybox. By default, it is set to the lowest + * priority (7) such that the Skybox is always rendered after the opaque objects, + * to reduce overdraw when depth culling is enabled. + * + * @param priority clamped to the range [0..7], defaults to 4; 7 is lowest priority + * (rendered last). + * + * @return Builder reference for chaining calls. + * + * @see RenderableManager::Builder::priority() + */ + Builder& priority(uint8_t priority) noexcept; + /** * Creates the Skybox object and returns a pointer to it. * diff --git a/package/ios/libs/filament/include/filament/Stream.h b/package/ios/libs/filament/include/filament/Stream.h index 6cafbacc..d3f914eb 100644 --- a/package/ios/libs/filament/include/filament/Stream.h +++ b/package/ios/libs/filament/include/filament/Stream.h @@ -23,6 +23,9 @@ #include #include +#include + +#include #include @@ -94,7 +97,7 @@ class UTILS_PUBLIC Stream : public FilamentAPI { * * To create a NATIVE stream, call the

stream
method on the builder. */ - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -136,6 +139,33 @@ class UTILS_PUBLIC Stream : public FilamentAPI { */ Builder& height(uint32_t height) noexcept; + /** + * Associate an optional name with this Stream for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this Stream + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this Stream for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this Stream + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + /** * Creates the Stream object and returns a pointer to it. * @@ -174,9 +204,10 @@ class UTILS_PUBLIC Stream : public FilamentAPI { * The 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. + * @param transform Optional transform matrix to apply to the image. */ void setAcquiredImage(void* UTILS_NONNULL image, - Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept; + Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata, math::mat3f const& transform = math::mat3f()) noexcept; /** * @see setAcquiredImage(void*, Callback, void*) @@ -187,10 +218,11 @@ class UTILS_PUBLIC Stream : public FilamentAPI { * 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. + * @param transform Optional transform matrix to apply to the image. */ void setAcquiredImage(void* UTILS_NONNULL image, backend::CallbackHandler* UTILS_NULLABLE handler, - Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata) noexcept; + Callback UTILS_NONNULL callback, void* UTILS_NULLABLE userdata, math::mat3f const& transform = math::mat3f()) noexcept; /** * Updates the size of the incoming stream. Whether this value is used is diff --git a/package/ios/libs/filament/include/filament/SwapChain.h b/package/ios/libs/filament/include/filament/SwapChain.h index 0af01afc..94721c0d 100644 --- a/package/ios/libs/filament/include/filament/SwapChain.h +++ b/package/ios/libs/filament/include/filament/SwapChain.h @@ -34,7 +34,7 @@ class Engine; /** * A swap chain represents an Operating System's *native* renderable surface. * - * Typically it's a native window or a view. Because a SwapChain is initialized from a + * Typically, it's a native window or a view. Because a SwapChain is initialized from a * native object, it is given to filament as a `void *`, which must be of the proper type * for each platform filament is running on. * @@ -115,7 +115,7 @@ class Engine; * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * SDL_SysWMinfo wmi; * SDL_VERSION(&wmi.version); - * ASSERT_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi), "SDL version unsupported!"); + * FILAMENT_CHECK_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi)) << "SDL version unsupported!"; * HDC nativeWindow = (HDC) wmi.info.win.hdc; * * using namespace filament; @@ -157,7 +157,7 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { /** * Requests a SwapChain with an alpha channel. */ - static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT; + static constexpr uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT; /** * This flag indicates that the swap chain may be used as a source surface @@ -167,13 +167,13 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { * @see * Renderer.copyFrame() */ - static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE; + static constexpr uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE; /** * Indicates that the native X11 window is an XCB window rather than an XLIB window. * This is ignored on non-Linux platforms and in builds that support only one X11 API. */ - static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB; + static constexpr uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB; /** * Indicates that the native window is a CVPixelBufferRef. @@ -185,7 +185,7 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { * Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and * CVPixelBufferRelease when the swap chain is destroyed. */ - static const uint64_t CONFIG_APPLE_CVPIXELBUFFER = + static constexpr uint64_t CONFIG_APPLE_CVPIXELBUFFER = backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER; /** @@ -233,6 +233,21 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { */ static constexpr uint64_t CONFIG_PROTECTED_CONTENT = backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT; + /** + * Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the + * given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is + * true. + * + * This is supported by EGL(Android) and Metal. Other GL platforms (GLX, WGL, etc) don't support + * it because the swapchain MSAA settings must be configured before window creation. + * + * With Metal, this flag should only be used when rendering a single View into a SwapChain. This + * flag is not supported when rendering multiple Filament Views into this SwapChain. + * + * @see isMSAASwapChainSupported(4) + */ + static constexpr uint64_t CONFIG_MSAA_4_SAMPLES = backend::SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES; + /** * Return whether createSwapChain supports the CONFIG_PROTECTED_CONTENT flag. * The default implementation returns false. @@ -251,55 +266,97 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { */ static bool isSRGBSwapChainSupported(Engine& engine) noexcept; + /** + * Return whether createSwapChain supports the CONFIG_MSAA_*_SAMPLES flag. + * The default implementation returns false. + * + * @param engine A pointer to the filament Engine + * @param samples The number of samples + * @return true if CONFIG_MSAA_*_SAMPLES is supported, false otherwise. + */ + static bool isMSAASwapChainSupported(Engine& engine, uint32_t samples) noexcept; + void* UTILS_NULLABLE getNativeWindow() const noexcept; /** - * FrameScheduledCallback is a callback function that notifies an application when Filament has - * completed processing a frame and that frame is ready to be scheduled for presentation. + * If this flag is passed to setFrameScheduledCallback, then the behavior of the default + * CallbackHandler (when nullptr is passed as the handler argument) is altered to call the + * callback on the Metal completion handler thread (as opposed to the main Filament thread). + * This flag also instructs the Metal backend to release the associated CAMetalDrawable on the + * completion handler thread. + * + * This flag has no effect if a custom CallbackHandler is passed or on backends other than Metal. + * + * @see setFrameScheduledCallback + */ + static constexpr uint64_t CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER = 1; + + /** + * FrameScheduledCallback is a callback function that notifies an application about the status + * of a frame after Filament has finished its processing. + * + * The exact timing and semantics of this callback differ depending on the graphics backend in + * use. + * + * Metal Backend + * ============= + * + * With the Metal backend, this callback signifies that Filament has completed all CPU-side + * processing for a frame and the frame is ready to be scheduled for presentation. * * Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain. - * If a SwapChain::FrameScheduledCallback is set, however, the application bares the - * responsibility of scheduling a frame for presentation by calling the backend::PresentCallable - * passed to the callback function. Currently this functionality is only supported by the Metal - * backend. + * If a SwapChain::FrameScheduledCallback is set, however, the application bears the + * responsibility of scheduling the frame for presentation by calling the + * backend::PresentCallable passed to the callback function. In this mode, Filament will *not* + * automatically schedule the frame for presentation. * - * A FrameScheduledCallback can be set on an individual SwapChain through - * SwapChain::setFrameScheduledCallback. If the callback is set, then the SwapChain will *not* - * automatically schedule itself for presentation. Instead, the application must call the - * PresentCallable passed to the FrameScheduledCallback. + * When using the Metal backend, if your application delays the call to the PresentCallable + * (e.g., by invoking it on a separate thread), you must ensure all PresentCallables have been + * called before shutting down the Filament Engine. You can guarantee this by calling + * Engine::flushAndWait() before Engine::shutdown(). This is necessary to ensure the Engine has + * a chance to clean up all memory related to frame presentation. + * + * Other Backends (OpenGL, Vulkan, WebGPU) + * ======================================= * - * There may be only one FrameScheduledCallback set per SwapChain. A call to - * SwapChain::setFrameScheduledCallback will overwrite any previous FrameScheduledCallbacks set - * on the same SwapChain. + * On other backends, this callback serves as a notification that Filament has completed all + * CPU-side processing for a frame. Filament proceeds with its normal presentation logic + * automatically, and the PresentCallable passed to the callback is a no-op that can be safely + * ignored. * - * If your application delays the call to the PresentCallable by, for example, calling it on a - * separate thread, you must ensure all PresentCallables have been called before shutting down - * the Filament Engine. You can do this by issuing an Engine::flushAndWait before calling - * Engine::shutdown. This is necessary to ensure the Filament Engine has had a chance to clean - * up all memory related to frame presentation. + * General Behavior + * ================ * - * @param callback A callback, or nullptr to unset. - * @param user An optional pointer to user data passed to the callback function. + * A FrameScheduledCallback can be set on an individual SwapChain through + * SwapChain::setFrameScheduledCallback. Each SwapChain can have only one callback set per + * frame. If setFrameScheduledCallback is called multiple times on the same SwapChain before + * Renderer::endFrame(), the most recent call effectively overwrites any previously set + * callback. + * + * The callback set by setFrameScheduledCallback is "latched" when Renderer::endFrame() is + * executed. At this point, the callback is fixed for the frame that was just encoded. + * Subsequent calls to setFrameScheduledCallback after endFrame() will apply to the next frame. * - * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other - * backends ignore the callback (which will never be called) and proceed normally. + * Use \c setFrameScheduledCallback() (with default arguments) to unset the callback. * - * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. + * @param handler Handler to dispatch the callback or nullptr for the default handler. + * @param callback Callback to be invoked when the frame processing is complete. + * @param flags See CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER * + * @see CallbackHandler * @see PresentCallable */ - void setFrameScheduledCallback(FrameScheduledCallback UTILS_NULLABLE callback, - void* UTILS_NULLABLE user = nullptr); + void setFrameScheduledCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr, + FrameScheduledCallback&& callback = {}, uint64_t flags = 0); /** - * Returns the SwapChain::FrameScheduledCallback that was previously set with - * SwapChain::setFrameScheduledCallback, or nullptr if one is not set. + * Returns whether this SwapChain currently has a FrameScheduledCallback set. * - * @return the previously-set FrameScheduledCallback, or nullptr + * @return true, if the last call to setFrameScheduledCallback set a callback * * @see SwapChain::setFrameCompletedCallback */ - UTILS_NULLABLE FrameScheduledCallback getFrameScheduledCallback() const noexcept; + bool isFrameScheduledCallbackSet() const noexcept; /** * FrameCompletedCallback is a callback function that notifies an application when a frame's diff --git a/package/ios/libs/filament/include/filament/Sync.h b/package/ios/libs/filament/include/filament/Sync.h new file mode 100644 index 00000000..0398ced6 --- /dev/null +++ b/package/ios/libs/filament/include/filament/Sync.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2025 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_SYNC_H +#define TNT_FILAMENT_SYNC_H + +#include + +#include +#include +#include + +namespace filament { + +class UTILS_PUBLIC Sync : public FilamentAPI { +public: + using CallbackHandler = backend::CallbackHandler; + using Callback = backend::Platform::SyncCallback; + + /** + * Fetches a handle to the external, platform-specific representation of + * this sync object. + * + * @param handler A handler for the callback that will receive the handle + * @param callback A callback that will receive the handle when ready + * @param userData Data to be passed to the callback so that the application + * can identify what frame the sync is relevant to. + * @return The external handle for the Sync. This is valid destroy() is + * called on this Sync object. + */ + void getExternalHandle(CallbackHandler* handler, Callback callback, void* userData) noexcept; + +protected: + // prevent heap allocation + ~Sync() = default; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_SYNC_H diff --git a/package/ios/libs/filament/include/filament/Texture.h b/package/ios/libs/filament/include/filament/Texture.h index 8a27f831..f7d3fcbf 100644 --- a/package/ios/libs/filament/include/filament/Texture.h +++ b/package/ios/libs/filament/include/filament/Texture.h @@ -23,11 +23,15 @@ #include #include +#include #include +#include +#include #include +#include #include #include @@ -70,7 +74,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { struct BuilderDetails; public: - static constexpr const size_t BASE_LEVEL = 0; + static constexpr size_t BASE_LEVEL = 0; //! Face offsets for all faces of a cubemap struct FaceOffsets; @@ -84,35 +88,42 @@ class UTILS_PUBLIC Texture : public FilamentAPI { using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format using Usage = backend::TextureUsage; //!< Usage affects texel layout using Swizzle = backend::TextureSwizzle; //!< Texture swizzle + using ExternalImageHandle = backend::Platform::ExternalImageHandle; + using ExternalImageHandleRef = backend::Platform::ExternalImageHandleRef; + using AsyncCompletionCallback = + std::function; + using AsyncCallId = backend::AsyncCallId; - /** @return whether a backend supports a particular format. */ + /** @return Whether a backend supports a particular format. */ static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept; - /** @return whether this backend supports protected textures. */ + /** @return Whether a backend supports mipmapping of a particular format. */ + static bool isTextureFormatMipmappable(Engine& engine, InternalFormat format) noexcept; + + /** @return Whether particular format is compressed */ + static bool isTextureFormatCompressed(InternalFormat format) noexcept; + + /** @return Whether this backend supports protected textures. */ static bool isProtectedTexturesSupported(Engine& engine) noexcept; - /** @return whether a backend supports texture swizzling. */ + /** @return Whether a backend supports texture swizzling. */ static bool isTextureSwizzleSupported(Engine& engine) noexcept; - static size_t computeTextureDataSize(Texture::Format format, Texture::Type type, + static size_t computeTextureDataSize(Format format, Type type, size_t stride, size_t height, size_t alignment) noexcept; + /** @return Whether a combination of texture format, pixel format and type is valid. */ + static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept; - /** - * Options for environment prefiltering into reflection map - * - * @see generatePrefilterMipmap() - */ - struct PrefilterOptions { - uint16_t sampleCount = 8; //!< sample count used for filtering - bool mirror = true; //!< whether the environment must be mirrored - private: - UTILS_UNUSED uintptr_t reserved[3] = {}; - }; + /** @return the maximum size in texels of a texture of type \p type. At least 2048 for + * 2D textures, 256 for 3D textures. */ + static size_t getMaxTextureSize(Engine& engine, Sampler type) noexcept; + /** @return the maximum number of layers supported by texture arrays. At least 256. */ + static size_t getMaxArrayTextureLayers(Engine& engine) noexcept; //! Use Builder to construct a Texture object instance - class Builder : public BuilderBase { + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -156,6 +167,20 @@ class UTILS_PUBLIC Texture : public FilamentAPI { */ Builder& levels(uint8_t levels) noexcept; + /** + * Specifies the numbers of samples used for MSAA (Multisample Anti-Aliasing). + * + * Calling this method implicitly indicates the texture is used as a render target. Hence, + * this method should not be used in conjunction with other methods that are semantically + * conflicting like `setImage`. + * + * If this is invoked for array textures, it means this texture is used for multiview. + * + * @param samples Number of samples for this texture. + * @return This Builder, for chaining calls. + */ + Builder& samples(uint8_t samples) noexcept; + /** * Specifies the type of sampler to use. * @param target Sampler type @@ -202,6 +227,71 @@ class UTILS_PUBLIC Texture : public FilamentAPI { */ Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept; + /** + * Associate an optional name with this Texture for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this Texture + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this Texture for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this Texture + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + + /** + * Creates an external texture. The content must be set using setExternalImage(). + * The sampler can be SAMPLER_EXTERNAL or SAMPLER_2D depending on the format. Generally + * YUV formats must use SAMPLER_EXTERNAL. This depends on the backend features and is not + * validated. + * + * If the Sampler is set to SAMPLER_EXTERNAL, external() is implied. + * + * @return + */ + Builder& external() noexcept; + + /** + * Specifies a callback that will execute once the resource's data has been fully allocated + * within the GPU memory. This enables the resource creation process to be handled + * asynchronously. + * + * Any asynchronous calls made during a resource's asynchronous creation (using this method) + * are safe because they are queued and executed in sequence. However, invoking regular + * methods on the same resource before it's fully ready is unsafe and may cause undefined + * behavior. Users can call the `isCreationComplete()` method for the resource to confirm + * when the resource is ready for regular API calls. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * This method and the `external()` method are mutually exclusive. You cannot use both + * because external texture's contents are filled later by calling `setExternalImage()`. + * + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * @return This Builder, for chaining calls. + */ + Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback = nullptr, + void* UTILS_NULLABLE user = nullptr) noexcept; + /** * Creates the Texture object and returns a pointer to it. * @@ -332,7 +422,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * uint32_t width, uint32_t height, uint32_t depth, * PixelBufferDescriptor&& buffer) */ - inline void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const { + void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const { setImage(engine, level, 0, 0, 0, uint32_t(getWidth(level)), uint32_t(getHeight(level)), 1, std::move(buffer)); } @@ -345,7 +435,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * uint32_t width, uint32_t height, uint32_t depth, * PixelBufferDescriptor&& buffer) */ - inline void setImage(Engine& engine, size_t level, + void setImage(Engine& engine, size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& buffer) const { setImage(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer)); @@ -378,9 +468,108 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const; + /** + * An asynchronous version of `setImage()`. + * Updates a sub-image of a 3D texture or 2D texture array for a level. Cubemaps are treated + * like a 2D array of six layers. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param engine Engine this texture is associated to. + * @param level Level to set the image for. + * @param xoffset Left offset of the sub-region to update. + * @param yoffset Bottom offset of the sub-region to update. + * @param zoffset Depth offset of the sub-region to update. + * @param width Width of the sub-region to update. + * @param height Height of the sub-region to update. + * @param depth Depth of the sub-region to update. + * @param buffer Client-side buffer containing the image to set. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention \p level must be less than getLevels(). + * @attention \p buffer's Texture::Format must match that of getFormat(). + * @attention This Texture instance must use Sampler::SAMPLER_3D, Sampler::SAMPLER_2D_ARRAY + * or Sampler::SAMPLER_CUBEMAP. + * + * @see Builder::sampler() + */ + AsyncCallId setImageAsync(Engine& engine, size_t level, + uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + uint32_t width, uint32_t height, uint32_t depth, + PixelBufferDescriptor&& buffer, + backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr) const; + + /** + * inline helper to update a 2D texture asynchronously + * + * @see setImageAsync(Engine& engine, size_t level, + * uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + * uint32_t width, uint32_t height, uint32_t depth, + * PixelBufferDescriptor&& buffer, + * backend::CallbackHandler* UTILS_NULLABLE handler, + * AsyncCompletionCallback callback, void* user) + */ + AsyncCallId setImageAsync(Engine& engine, size_t level, PixelBufferDescriptor&& buffer, + backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr) const { + return setImageAsync(engine, level, 0, 0, 0, + uint32_t(getWidth(level)), uint32_t(getHeight(level)), 1, std::move(buffer), + handler, std::move(callback), user); + } + + /** + * inline helper to update a 2D texture asynchronously + * + * @see setImageAsync(Engine& engine, size_t level, + * uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + * uint32_t width, uint32_t height, uint32_t depth, + * PixelBufferDescriptor&& buffer, + * backend::CallbackHandler* UTILS_NULLABLE handler, + * AsyncCompletionCallback callback, void* user) + */ + AsyncCallId setImageAsync(Engine& engine, size_t level, + uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, + PixelBufferDescriptor&& buffer, + backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, + void* UTILS_NULLABLE user = nullptr) const { + return setImageAsync(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer), + handler, std::move(callback), user); + } + + /** + * Specify the external image to associate with this Texture. Typically, the external + * image is OS specific, and can be a video or camera frame. + * There are many restrictions when using an external image as a texture, such as: + * - only the level of detail (lod) 0 can be specified + * - only nearest or linear filtering is supported + * - the size and format of the texture is defined by the external image + * - only the CLAMP_TO_EDGE wrap mode is supported + * + * @param engine Engine this texture is associated to. + * @param image An opaque handle to a platform specific image. It must be created using Platform + * specific APIs. For example PlatformEGL::createExternalImage(EGLImageKHR eglImage) + * + * @see PlatformEGL::createExternalImage + * @see PlatformEGLAndroid::createExternalImage + * @see PlatformCocoaGL::createExternalImage + * @see PlatformCocoaTouchGL::createExternalImage + */ + void setExternalImage(Engine& engine, ExternalImageHandleRef image) noexcept; /** - * Specify the external image to associate with this Texture. Typically the external + * Specify the external image to associate with this Texture. Typically, the external * image is OS specific, and can be a video or camera frame. * There are many restrictions when using an external image as a texture, such as: * - only the level of detail (lod) 0 can be specified @@ -401,11 +590,13 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * * @see Builder::sampler() * + * @deprecated Instead, use setExternalImage(Engine& engine, ExternalImageHandleRef image) */ + UTILS_DEPRECATED void setExternalImage(Engine& engine, void* UTILS_NONNULL image) noexcept; /** - * Specify the external image and plane to associate with this Texture. Typically the external + * Specify the external image and plane to associate with this Texture. Typically, the external * image is OS specific, and can be a video or camera frame. When using this method, the * external image must be a planar type (such as a YUV camera frame). The plane parameter * selects which image plane is bound to this texture. @@ -436,7 +627,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void setExternalImage(Engine& engine, void* UTILS_NONNULL image, size_t plane) noexcept; /** - * Specify the external stream to associate with this Texture. Typically the external + * Specify the external stream to associate with this Texture. Typically, the external * stream is OS specific, and can be a video or camera stream. * There are many restrictions when using an external stream as a texture, such as: * - only the level of detail (lod) 0 can be specified @@ -447,7 +638,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { * @param stream A Stream object * * @attention \p engine must be the instance passed to Builder::build() - * @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL or it has no effect + * @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL, or it has no effect * * @see Builder::sampler(), Stream * @@ -467,43 +658,16 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void generateMipmaps(Engine& engine) const noexcept; /** - * Creates a reflection map from an environment map. - * - * This is a utility function that replaces calls to Texture::setImage(). - * The provided environment map is processed and all mipmap levels are populated. The - * processing is similar to the offline tool `cmgen` as a lower quality setting. - * - * This function is intended to be used when the environment cannot be processed offline, - * for instance if it's generated at runtime. - * - * The source data must obey to some constraints: - * - the data type must be PixelDataFormat::RGB - * - the data format must be one of - * - PixelDataType::FLOAT - * - PixelDataType::HALF - * - * The current texture must be a cubemap - * - * The reflections cubemap's internal format cannot be a compressed format. - * - * The reflections cubemap's dimension must be a power-of-two. + * This non-blocking method checks if the resource has finished creation. If the resource + * creation was initiated asynchronously, it will return true only after all related + * asynchronous tasks are complete. If the resource was created normally without using async + * method, it will always return true. * - * @warning This operation is computationally intensive, especially with large environments and - * is currently synchronous. Expect about 1ms for a 16x16 cubemap. - * - * @param engine Reference to the filament::Engine to associate this IndirectLight with. - * @param buffer Client-side buffer containing the images to set. - * @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets - * are specified in the following order: +x, -x, +y, -y, +z, -z - * @param options Optional parameter to controlling user-specified quality and options. - * - * @exception utils::PreConditionPanic if the source data constraints are not respected. + * @return Whether the resource is created. * + * @see Builder::async() */ - void generatePrefilterMipmap(Engine& engine, - PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, - PrefilterOptions const* UTILS_NULLABLE options = nullptr); - + bool isCreationComplete() const noexcept; /** @deprecated */ struct FaceOffsets { diff --git a/package/ios/libs/filament/include/filament/TextureSampler.h b/package/ios/libs/filament/include/filament/TextureSampler.h index ba5e5534..ea40a705 100644 --- a/package/ios/libs/filament/include/filament/TextureSampler.h +++ b/package/ios/libs/filament/include/filament/TextureSampler.h @@ -154,8 +154,8 @@ class UTILS_PUBLIC TextureSampler { /** * This controls anisotropic filtering. - * @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0. - * The maximum permissible value is 7. + * @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 1. + * The maximum permissible value is 128. */ void setAnisotropy(float anisotropy) noexcept { const int log2 = ilogbf(anisotropy > 0 ? anisotropy : -anisotropy); diff --git a/package/ios/libs/filament/include/filament/ToneMapper.h b/package/ios/libs/filament/include/filament/ToneMapper.h index e89e702c..cff0576e 100644 --- a/package/ios/libs/filament/include/filament/ToneMapper.h +++ b/package/ios/libs/filament/include/filament/ToneMapper.h @@ -68,6 +68,21 @@ struct UTILS_PUBLIC ToneMapper { * function applied ("linear") */ virtual math::float3 operator()(math::float3 c) const noexcept = 0; + + /** + * If true, then this function holds that f(x) = vec3(f(x.r), f(x.g), f(x.b)) + * + * This may be used to indicate that the color grading's LUT only requires a 1D texture instead + * of a 3D texture, potentially saving a significant amount of memory and generation time. + */ + virtual bool isOneDimensional() const noexcept { return false; } + + /** + * True if this tonemapper only works in low-dynamic-range. + * + * This may be used to indicate that the color grading's LUT doesn't need to be log encoded. + */ + virtual bool isLDR() const noexcept { return false; } }; /** @@ -79,6 +94,8 @@ struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper { ~LinearToneMapper() noexcept final; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return true; } + bool isLDR() const noexcept override { return true; } }; /** @@ -91,6 +108,8 @@ struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper { ~ACESToneMapper() noexcept final; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; /** @@ -104,6 +123,8 @@ struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper { ~ACESLegacyToneMapper() noexcept final; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; /** @@ -117,6 +138,8 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper { ~FilmicToneMapper() noexcept final; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return true; } + bool isLDR() const noexcept override { return false; } }; /** @@ -129,6 +152,8 @@ struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper { ~PBRNeutralToneMapper() noexcept final; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; /** @@ -150,6 +175,8 @@ struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper { ~AgxToneMapper() noexcept final; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } AgxLook look; }; @@ -194,6 +221,8 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper { GenericToneMapper& operator=(GenericToneMapper&& rhs) noexcept; math::float3 operator()(math::float3 x) const noexcept override; + bool isOneDimensional() const noexcept override { return true; } + bool isLDR() const noexcept override { return false; } /** Returns the contrast of the curve as a strictly positive value. */ float getContrast() const noexcept; @@ -256,6 +285,8 @@ struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper { ~DisplayRangeToneMapper() noexcept override; math::float3 operator()(math::float3 c) const noexcept override; + bool isOneDimensional() const noexcept override { return false; } + bool isLDR() const noexcept override { return false; } }; } // namespace filament diff --git a/package/ios/libs/filament/include/filament/VertexBuffer.h b/package/ios/libs/filament/include/filament/VertexBuffer.h index fccbd004..b29c7f9a 100644 --- a/package/ios/libs/filament/include/filament/VertexBuffer.h +++ b/package/ios/libs/filament/include/filament/VertexBuffer.h @@ -26,7 +26,9 @@ #include #include +#include +#include #include #include @@ -60,8 +62,12 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { public: using AttributeType = backend::ElementType; using BufferDescriptor = backend::BufferDescriptor; + using AsyncCompletionCallback = + std::function; + using AsyncCallId = backend::AsyncCallId; - class Builder : public BuilderBase { + + class Builder : public BuilderBase, public BuilderNameMixin { friend struct BuilderDetails; public: Builder() noexcept; @@ -158,6 +164,56 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { */ Builder& advancedSkinning(bool enabled) noexcept; + /** + * Associate an optional name with this VertexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. The name is + * truncated to a maximum of 128 characters. + * + * The name string is copied during this method so clients may free its memory after + * the function returns. + * + * @param name A string to identify this VertexBuffer + * @param len Length of name, should be less than or equal to 128 + * @return This Builder, for chaining calls. + * @deprecated Use name(utils::StaticString const&) instead. + */ + UTILS_DEPRECATED + Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept; + + /** + * Associate an optional name with this VertexBuffer for debugging purposes. + * + * name will show in error messages and should be kept as short as possible. + * + * @param name A string literal to identify this VertexBuffer + * @return This Builder, for chaining calls. + */ + Builder& name(utils::StaticString const& name) noexcept; + + /** + * Specifies a callback that will execute once the resource's data has been fully allocated + * within the GPU memory. This enables the resource creation process to be handled + * asynchronously. + * + * Any asynchronous calls made during a resource's asynchronous creation (using this method) + * are safe because they are queued and executed in sequence. However, invoking regular + * methods on the same resource before it's fully ready is unsafe and may cause undefined + * behavior. Users can call the `isCreationComplete()` method for the resource to confirm + * when the resource is ready for regular API calls. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * @return This Builder, for chaining calls. + */ + Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback = nullptr, + void* UTILS_NULLABLE user = nullptr) noexcept; + /** * Creates the VertexBuffer object and returns a pointer to it. * @@ -182,7 +238,7 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { size_t getVertexCount() const noexcept; /** - * Asynchronously copy-initializes the specified buffer from the given buffer data. + * copy-initializes the specified buffer from the given buffer data. * * Do not use this if you called enableBufferObjects() on the Builder. * @@ -193,11 +249,41 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { * index \p bufferIndex. BufferDescriptor points to raw, untyped data that will * be copied as-is into the buffer. * @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex - * buffer set. + * buffer set. Must be multiple of 4. */ void setBufferAt(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + /** + * An asynchronous version of `setBufferAt()`. + * Asynchronously copy-initializes the specified buffer from the given buffer data. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * Do not use this if you called enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param buffer A BufferDescriptor representing the data used to initialize the buffer at + * index \p bufferIndex. BufferDescriptor points to raw, untyped data that will + * be copied as-is into the buffer. + * @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex + * buffer set. Must be multiple of 4. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + */ + AsyncCallId setBufferAtAsync(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer, + uint32_t byteOffset, backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, void* UTILS_NULLABLE user = nullptr); + /** * Swaps in the given buffer object. * @@ -211,6 +297,45 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI { void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* UTILS_NONNULL bufferObject); + /** + * An asynchronous version of `setBufferObjectAt()`. + * Swaps in the given buffer object. + * + * Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the + * asynchronous call. + * + * To use this method, the engine must be configured for asynchronous operation. Otherwise, + * calling async method will cause the program to terminate. + * + * To use this, you must first call enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param bufferObject The handle to the GPU data that will be used in this buffer slot. + * @param handler Handler to dispatch the callback or nullptr for the default handler + * @param callback A function to be called upon the completion of an asynchronous creation. + * @param user The custom data that will be passed as the second argument to the `callback`. + * + * @return An ID that the caller can use to cancel the operation. + */ + AsyncCallId setBufferObjectAtAsync(Engine& engine, uint8_t bufferIndex, + BufferObject const* UTILS_NONNULL bufferObject, + backend::CallbackHandler* UTILS_NULLABLE handler, + AsyncCompletionCallback callback, void* UTILS_NULLABLE user = nullptr); + + /** + * This non-blocking method checks if the resource has finished creation. If the resource + * creation was initiated asynchronously, it will return true only after all related + * asynchronous tasks are complete. If the resource was created normally without using async + * method, it will always return true. + * + * @return Whether the resource is created. + * + * @see Builder::async() + */ + bool isCreationComplete() const noexcept; + protected: // prevent heap allocation ~VertexBuffer() = default; diff --git a/package/ios/libs/filament/include/filament/View.h b/package/ios/libs/filament/include/filament/View.h index 3cdd527f..0240bafa 100644 --- a/package/ios/libs/filament/include/filament/View.h +++ b/package/ios/libs/filament/include/filament/View.h @@ -24,8 +24,10 @@ #include #include +#include #include +#include #include @@ -40,6 +42,7 @@ class CallbackHandler; class Camera; class ColorGrading; +class Engine; class MaterialInstance; class RenderTarget; class Scene; @@ -182,27 +185,55 @@ class UTILS_PUBLIC View : public FilamentAPI { * View.\n * The View doesn't take ownership of the Camera pointer (which * acts as a reference). + * If the camera isn't set, Renderer::render() will result in a no-op. * * @note * There is no reference-counting. * Make sure to dissociate a Camera from all Views before destroying it. */ - void setCamera(Camera* UTILS_NONNULL camera) noexcept; + void setCamera(Camera* UTILS_NULLABLE camera) noexcept; + + /** + * Returns whether a Camera is set. + * @return true if a camera is set. + * @see setCamera() + */ + bool hasCamera() const noexcept; /** * Returns the Camera currently associated with this View. - * @return A reference to the Camera associated to this View. + * Undefined behavior if hasCamera() is false. + * @return A reference to the Camera associated to this View if hasCamera() is true. + * @see hasCamera() */ Camera& getCamera() noexcept; /** * Returns the Camera currently associated with this View. + * Undefined behavior if hasCamera() is false. * @return A reference to the Camera associated to this View. + * @see hasCamera() */ Camera const& getCamera() const noexcept { return const_cast(this)->getCamera(); } + + /** + * Sets whether a channel must clear the depth buffer before all primitives are rendered. + * Channel depth clear is off by default for all channels. + * This is orthogonal to Renderer::setClearOptions(). + * @param channel between 0 and 7 + * @param enabled true to enable clear, false to disable + */ + void setChannelDepthClearEnabled(uint8_t channel, bool enabled) noexcept; + + /** + * @param channel between 0 and 7 + * @return true if this channel has depth clear enabled. + */ + bool isChannelDepthClearEnabled(uint8_t channel) const noexcept; + /** * Sets the blending mode used to draw the view into the SwapChain. * @@ -510,6 +541,14 @@ class UTILS_PUBLIC View : public FilamentAPI { */ DynamicResolutionOptions getDynamicResolutionOptions() const noexcept; + /** + * Returns the last dynamic resolution scale factor used by this view. This value is updated + * when Renderer::render(View*) is called + * @return a float2 where x is the horizontal and y the vertical scale factor. + * @see Renderer::render + */ + math::float2 getLastDynamicResolutionScale() const noexcept; + /** * Sets the rendering quality for this view. Refer to RenderQuality for more * information about the different settings available. @@ -533,12 +572,13 @@ class UTILS_PUBLIC View : public FilamentAPI { * visible -- in this case, using a larger value can improve performance. * e.g. when standing and looking straight, several meters of the ground * isn't visible and if lights are expected to shine there, there is no - * point using a short zLightNear. (Default 5m). + * point using a short zLightNear. This value is clamped between + * the camera near and far plane. (Default 5m). * * @param zLightFar Distance from the camera after which lights are not expected to be visible. * Similarly to zLightNear, setting this value properly can improve - * performance. (Default 100m). - * + * performance. This value is clamped between the camera near and far plane. + * (Default 100m). * * Together zLightNear and zLightFar must be chosen so that the visible influence of lights * is spread between these two values. @@ -561,6 +601,13 @@ class UTILS_PUBLIC View : public FilamentAPI { */ void setShadowType(ShadowType shadow) noexcept; + /** + * Returns the shadow mapping technique used by this View. + * + * @return value set by setShadowType(). + */ + ShadowType getShadowType() const noexcept; + /** * Sets VSM shadowing options that apply across the entire View. * @@ -652,6 +699,26 @@ class UTILS_PUBLIC View : public FilamentAPI { */ bool isFrontFaceWindingInverted() const noexcept; + /** + * Enables or disables transparent picking. Disabled by default. + * + * When transparent picking is enabled, View::pick() will pick from both + * transparent and opaque renderables. When disabled, View::pick() will only + * pick from opaque renderables. + * + * @param enabled true enables transparent picking, false disables it. + * + * @note Transparent picking will create an extra pass for rendering depth + * from both transparent and opaque renderables. + */ + void setTransparentPickingEnabled(bool enabled) noexcept; + + /** + * Returns true if transparent picking is enabled. + * See setTransparentPickingEnabled() for more information. + */ + bool isTransparentPickingEnabled() const noexcept; + /** * Enables use of the stencil buffer. * @@ -719,8 +786,31 @@ class UTILS_PUBLIC View : public FilamentAPI { void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept; //! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing. - Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept; + utils::FixedCapacityVector getDirectionalShadowCameras() const noexcept; + + //! debugging: enable or disable froxel visualisation for this view. + void setFroxelVizEnabled(bool enabled) noexcept; + + //! debugging: returns information about the froxel configuration + struct FroxelConfigurationInfo { + uint16_t width; + uint16_t height; + uint16_t depth; + uint32_t viewportWidth; + uint32_t viewportHeight; + math::uint2 froxelDimension; + float zLightFar; + float linearizer; + math::mat4f p; + math::float4 clipTransform; + }; + + struct FroxelConfigurationInfoWithAge { + FroxelConfigurationInfo info; + uint32_t age; + }; + FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept; /** Result of a picking query */ struct PickingQueryResult { @@ -871,6 +961,17 @@ class UTILS_PUBLIC View : public FilamentAPI { */ utils::Entity getFogEntity() const noexcept; + + /** + * When certain temporal features are used (e.g.: TAA or Screen-space reflections), the view + * keeps a history of previous frame renders associated with the Renderer the view was last + * used with. When switching Renderer, it may be necessary to clear that history by calling + * this method. Similarly, if the whole content of the screen change, like when a cut-scene + * starts, clearing the history might be needed to avoid artifacts due to the previous frame + * being very different. + */ + void clearFrameHistory(Engine& engine) noexcept; + /** * List of available ambient occlusion techniques * @deprecated use AmbientOcclusionOptions::enabled instead diff --git a/package/ios/libs/filament/include/geometry/TangentSpaceMesh.h b/package/ios/libs/filament/include/geometry/TangentSpaceMesh.h index 980e81ac..1872aaad 100644 --- a/package/ios/libs/filament/include/geometry/TangentSpaceMesh.h +++ b/package/ios/libs/filament/include/geometry/TangentSpaceMesh.h @@ -340,7 +340,7 @@ class TangentSpaceMesh { std::is_same::value || std::is_same::value>::type; template> - void getAux(AuxAttribute attribute, T* out, size_t stride = 0) const noexcept; + void getAux(AuxAttribute attribute, T* out, size_t stride = 0) const; /** * Get number of output triangles. diff --git a/package/ios/libs/filament/include/gltfio/Animator.h b/package/ios/libs/filament/include/gltfio/Animator.h index dd550938..e02a94a6 100644 --- a/package/ios/libs/filament/include/gltfio/Animator.h +++ b/package/ios/libs/filament/include/gltfio/Animator.h @@ -63,7 +63,7 @@ class UTILS_PUBLIC Animator { * This is useful if you have other instances that have the same skeleton as the animator * from this asset, and you want those instances to be animated by the same animation (e.g. clothing). * Usually you don't need this and using updateBoneMatrices() is enough. - * + * * @param instance The instance to update. */ void updateBoneMatricesForInstance(FilamentInstance* instance); diff --git a/package/ios/libs/filament/include/gltfio/AssetLoader.h b/package/ios/libs/filament/include/gltfio/AssetLoader.h index f516166a..bf650f69 100644 --- a/package/ios/libs/filament/include/gltfio/AssetLoader.h +++ b/package/ios/libs/filament/include/gltfio/AssetLoader.h @@ -38,6 +38,23 @@ namespace filament::gltfio { class NodeManager; +// Use this struct to enable mikktspace-based tangent-space computation. +/** + * \struct AssetConfigurationExtended AssetLoader.h gltfio/AssetLoader.h + * \brief extends struct AssetConfiguration + * Useful if client needs mikktspace tangent space computation. + * NOTE: Android, iOS, Web are not supported. And only disk-local glTF resources are supported. + */ +struct AssetConfigurationExtended { + //! Optional The same parameter as provided to \struct ResourceConfiguration ResourceLoader.h + //! gltfio/ResourceLoader.h + char const* gltfPath; + + //! Client can use this method to check if the extended implementation is supported on their + //! platform or not. + static bool isSupported(); +}; + /** * \struct AssetConfiguration AssetLoader.h gltfio/AssetLoader.h * \brief Construction parameters for AssetLoader. @@ -62,6 +79,10 @@ struct AssetConfiguration { //! Optional default node name for anonymous nodes char* defaultNodeName = nullptr; + + //! Optional to enable mikktspace tangents. Lifetime of struct only needs to be maintained for + // the duration of the constructor of AssetLoader. + AssetConfigurationExtended* ext = nullptr; }; /** diff --git a/package/ios/libs/filament/include/gltfio/MaterialProvider.h b/package/ios/libs/filament/include/gltfio/MaterialProvider.h index f62d667b..e4cf01b3 100644 --- a/package/ios/libs/filament/include/gltfio/MaterialProvider.h +++ b/package/ios/libs/filament/include/gltfio/MaterialProvider.h @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -93,10 +94,18 @@ struct alignas(4) MaterialKey { bool hasSheen : 1; bool hasIOR : 1; bool hasVolume : 1; - uint8_t padding : 5; + bool hasDispersion : 1; + bool hasSpecular : 1; + bool hasSpecularTexture : 1; + bool hasSpecularColorTexture : 1; + bool padding : 1; + // -- 32 bit boundary -- + uint8_t specularTextureUV; + uint8_t specularColorTextureUV; + uint16_t padding2; }; -static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected size."); +static_assert(sizeof(MaterialKey) == 20, "MaterialKey has unexpected size."); UTILS_WARNING_POP @@ -189,6 +198,7 @@ void processShaderString(std::string* shader, const UvMap& uvmap, * Creates a material provider that builds materials on the fly, composing GLSL at run time. * * @param optimizeShaders Optimizes shaders, but at significant cost to construction time. + * @param variantFilters Filter out variants that are not required. * @return New material provider that can build materials at run time. * * Requires \c libfilamat to be linked in. Not available in \c libgltfio_core. @@ -196,7 +206,8 @@ void processShaderString(std::string* shader, const UvMap& uvmap, * @see createUbershaderProvider */ UTILS_PUBLIC -MaterialProvider* createJitShaderProvider(Engine* engine, bool optimizeShaders = false); +MaterialProvider* createJitShaderProvider(Engine* engine, bool optimizeShaders = false, + utils::FixedCapacityVector const& variantFilters = {}); /** * Creates a material provider that loads a small set of pre-built materials. diff --git a/package/ios/libs/filament/include/gltfio/TextureProvider.h b/package/ios/libs/filament/include/gltfio/TextureProvider.h index ff497af2..0015c86a 100644 --- a/package/ios/libs/filament/include/gltfio/TextureProvider.h +++ b/package/ios/libs/filament/include/gltfio/TextureProvider.h @@ -179,6 +179,19 @@ TextureProvider* createStbProvider(filament::Engine* engine); */ TextureProvider* createKtx2Provider(filament::Engine* engine); +/** + * If webp support is enabled at build time, creates a decoder that can handle "image/webp" + * lossless and lossy content. + * If webp support is not enabled at build time, returns nullptr. + */ +TextureProvider* createWebpProvider(filament::Engine* engine); + +/** + * Indicates if build-time webp support was included. + * Returns true if it was and false if not. + */ +bool isWebpSupported(); + } // namespace filament::gltfio template<> struct utils::EnableBitMaskOperators diff --git a/package/ios/libs/filament/include/gltfio/materials/uberarchive.h b/package/ios/libs/filament/include/gltfio/materials/uberarchive.h index b36ddd36..3e443947 100644 --- a/package/ios/libs/filament/include/gltfio/materials/uberarchive.h +++ b/package/ios/libs/filament/include/gltfio/materials/uberarchive.h @@ -5,9 +5,10 @@ extern "C" { extern const uint8_t UBERARCHIVE_PACKAGE[]; - extern int UBERARCHIVE_DEFAULT_OFFSET; - extern int UBERARCHIVE_DEFAULT_SIZE; } + +#define UBERARCHIVE_DEFAULT_OFFSET 0 +#define UBERARCHIVE_DEFAULT_SIZE 12414810 #define UBERARCHIVE_DEFAULT_DATA (UBERARCHIVE_PACKAGE + UBERARCHIVE_DEFAULT_OFFSET) #endif diff --git a/package/ios/libs/filament/include/ibl/CubemapIBL.h b/package/ios/libs/filament/include/ibl/CubemapIBL.h index 925e27c0..ae8fcb08 100644 --- a/package/ios/libs/filament/include/ibl/CubemapIBL.h +++ b/package/ios/libs/filament/include/ibl/CubemapIBL.h @@ -54,7 +54,7 @@ class UTILS_PUBLIC CubemapIBL { * @param updater a callback for the caller to track progress */ static void roughnessFilter( - utils::JobSystem& js, Cubemap& dst, const utils::Slice& levels, + utils::JobSystem& js, Cubemap& dst, utils::Slice levels, float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter, Progress updater = nullptr, void* userdata = nullptr); diff --git a/package/ios/libs/filament/include/math/TQuatHelpers.h b/package/ios/libs/filament/include/math/TQuatHelpers.h index 0439b971..7ff641b0 100644 --- a/package/ios/libs/filament/include/math/TQuatHelpers.h +++ b/package/ios/libs/filament/include/math/TQuatHelpers.h @@ -259,10 +259,11 @@ class TQuatFunctions { return normalize(lerp(d < 0 ? -p : p, q, t)); } const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| - const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1))); + const T cos_a = math::clamp(absd / npq, T(-1), T(1)); + const T a = std::acos(cos_a); const T a0 = a * (1 - t); const T a1 = a * t; - const T sina = sin(a); + const T sina = std::sqrt(T(1) - cos_a * cos_a); if (sina < value_eps) { return normalize(lerp(p, q, t)); } diff --git a/package/ios/libs/filament/include/math/compiler.h b/package/ios/libs/filament/include/math/compiler.h index 634e2077..a7d85168 100644 --- a/package/ios/libs/filament/include/math/compiler.h +++ b/package/ios/libs/filament/include/math/compiler.h @@ -122,6 +122,11 @@ struct is_arithmetic : std::integral_constant::value || std::is_floating_point::value> { }; +template +struct is_floating_point : std::integral_constant::value> { +}; + } // filament::math #endif // TNT_MATH_COMPILER_H diff --git a/package/ios/libs/filament/include/math/fast.h b/package/ios/libs/filament/include/math/fast.h index 85b990d2..9988cfd2 100644 --- a/package/ios/libs/filament/include/math/fast.h +++ b/package/ios/libs/filament/include/math/fast.h @@ -50,7 +50,7 @@ constexpr T MATH_PURE cos(T x) noexcept { // x between -pi and pi template::value>> constexpr T MATH_PURE sin(T x) noexcept { - return filament::math::fast::cos(x - T(F_PI_2)); + return fast::cos(x - T(F_PI_2)); } constexpr inline float MATH_PURE ilog2(float x) noexcept { diff --git a/package/ios/libs/filament/include/math/half.h b/package/ios/libs/filament/include/math/half.h index d792e1bf..4578708b 100644 --- a/package/ios/libs/filament/include/math/half.h +++ b/package/ios/libs/filament/include/math/half.h @@ -164,20 +164,20 @@ inline constexpr half operator""_h(long double v) { return half( static_cast(v) ); } -template<> struct is_arithmetic : public std::true_type {}; +template<> struct is_arithmetic : public std::true_type {}; + +template<> struct is_floating_point : public std::true_type {}; } // namespace math } // namespace filament namespace std { -template<> struct is_floating_point : public std::true_type {}; - -// note: this shouldn't be needed (is_floating_point<> is enough) but some version of msvc need it -// This stopped working with MSVC 2019 16.4, so we specialize our own version of is_arithmetic in +// Remove the standard template specializations for filament::math::half +// Clang 20 explicitly blocks customizing standard type traits +// Instead, use the traits defined in the math:: namespace +// This avoids compatibility issues with Clang 20 and MSVC 2019 16.4+ // the math::filament namespace (see above). -template<> struct is_arithmetic : public std::true_type {}; - template<> class numeric_limits { public: diff --git a/package/ios/libs/filament/include/math/mat2.h b/package/ios/libs/filament/include/math/mat2.h index dba9ca47..d12aa532 100644 --- a/package/ios/libs/filament/include/math/mat2.h +++ b/package/ios/libs/filament/include/math/mat2.h @@ -225,7 +225,7 @@ class MATH_EMPTY_BASES TMat22 : * Rotate by radians in the 2D plane */ static TMat22 rotate(T radian) noexcept { - TMat22 r(TMat22::NO_INIT); + TMat22 r(NO_INIT); T c = std::cos(radian); T s = std::sin(radian); r[0][0] = c; diff --git a/package/ios/libs/filament/include/math/mat3.h b/package/ios/libs/filament/include/math/mat3.h index 035865fe..57ad7ec3 100644 --- a/package/ios/libs/filament/include/math/mat3.h +++ b/package/ios/libs/filament/include/math/mat3.h @@ -256,7 +256,7 @@ class MATH_EMPTY_BASES TMat33 : */ friend inline constexpr TMat33 orthogonalize(const TMat33& m) noexcept { - TMat33 ret(TMat33::NO_INIT); + TMat33 ret(NO_INIT); ret[0] = normalize(m[0]); ret[2] = normalize(cross(ret[0], m[1])); ret[1] = normalize(cross(ret[2], ret[0])); @@ -479,7 +479,7 @@ constexpr TQuaternion TMat33::packTangentFrame(const TMat33& m, size_t template constexpr details::TMat33 prescaleForNormals(const details::TMat33& m) noexcept { return m * details::TMat33( - 1.0 / std::sqrt(max(float3{length2(m[0]), length2(m[1]), length2(m[2])}))); + T(1.0) / std::sqrt(max(float3{length2(m[0]), length2(m[1]), length2(m[2])}))); } // ---------------------------------------------------------------------------------------- diff --git a/package/ios/libs/filament/include/math/mat4.h b/package/ios/libs/filament/include/math/mat4.h index 57058539..7eb50e35 100644 --- a/package/ios/libs/filament/include/math/mat4.h +++ b/package/ios/libs/filament/include/math/mat4.h @@ -498,10 +498,10 @@ constexpr TMat44 TMat44::frustum(T left, T right, T bottom, T top, T near, } template -TMat44 TMat44::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) noexcept { +TMat44 TMat44::perspective(T fov, T aspect, T near, T far, Fov direction) noexcept { T h, w; - if (direction == TMat44::Fov::VERTICAL) { + if (direction == Fov::VERTICAL) { h = std::tan(fov * F_PI / 360.0f) * near; w = h * aspect; } else { diff --git a/package/ios/libs/filament/include/private/backend/VirtualMachineEnv.h b/package/ios/libs/filament/include/private/backend/VirtualMachineEnv.h new file mode 100644 index 00000000..c4352391 --- /dev/null +++ b/package/ios/libs/filament/include/private/backend/VirtualMachineEnv.h @@ -0,0 +1,64 @@ +/* + * 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_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H +#define TNT_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H + +#include +#include + +#include + +namespace filament { + +class VirtualMachineEnv { +public: + // must be called before VirtualMachineEnv::get() from a thread that is attached to the JavaVM + static jint JNI_OnLoad(JavaVM* vm); + + // must be called on backend thread + static VirtualMachineEnv& get() noexcept; + + // can be called from any thread that already has a JniEnv + static JNIEnv* getThreadEnvironment(); + + // must be called from the backend thread + JNIEnv* getEnvironment() noexcept { + JNIEnv* env = mJniEnv; + if (UTILS_UNLIKELY(!env)) { + return getEnvironmentSlow(); + } + return env; + } + + static void handleException(JNIEnv* env) noexcept; + +private: + explicit VirtualMachineEnv(JavaVM* vm) noexcept; + ~VirtualMachineEnv() noexcept; + JNIEnv* getEnvironmentSlow(); + + static utils::Mutex sLock; + static JavaVM* sVirtualMachine; + static JavaVM* getVirtualMachine(); + + JNIEnv* mJniEnv = nullptr; + JavaVM* mVirtualMachine = nullptr; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_ANDROID_VIRTUAL_MACHINE_ENV_H diff --git a/package/ios/libs/filament/include/uberz/WritableArchive.h b/package/ios/libs/filament/include/uberz/WritableArchive.h index e511d95f..b383b70c 100644 --- a/package/ios/libs/filament/include/uberz/WritableArchive.h +++ b/package/ios/libs/filament/include/uberz/WritableArchive.h @@ -56,7 +56,7 @@ class WritableArchive { utils::FixedCapacityVector package; Shading shadingModel; BlendingMode blendingMode; - tsl::robin_map flags; + tsl::robin_map flags; }; utils::FixedCapacityVector mMaterials; diff --git a/package/ios/libs/filament/include/utils/Allocator.h b/package/ios/libs/filament/include/utils/Allocator.h index 7b9978f5..d74b8cc8 100644 --- a/package/ios/libs/filament/include/utils/Allocator.h +++ b/package/ios/libs/filament/include/utils/Allocator.h @@ -18,7 +18,6 @@ #define TNT_UTILS_ALLOCATOR_H #include -#include #include #include @@ -36,22 +35,22 @@ namespace utils { namespace pointermath { -template -static inline P* add(P* a, T b) noexcept { - return (P*)(uintptr_t(a) + uintptr_t(b)); +template +static P* add(P* a, T b) noexcept { + return (P*) (uintptr_t(a) + uintptr_t(b)); } -template -static inline P* align(P* p, size_t alignment) noexcept { +template +static P* align(P* p, size_t alignment) noexcept { // alignment must be a power-of-two - assert_invariant(alignment && !(alignment & alignment-1)); - return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1)); + assert(alignment && !(alignment & alignment-1)); + return (P*) ((uintptr_t(p) + alignment - 1) & ~(alignment - 1)); } -template -static inline P* align(P* p, size_t alignment, size_t offset) noexcept { +template +static P* align(P* p, size_t alignment, size_t offset) noexcept { P* const r = align(add(p, offset), alignment); - assert_invariant(r >= add(p, offset)); + assert(r >= add(p, offset)); return r; } @@ -102,7 +101,7 @@ class LinearAllocator { // free memory back to the specified point void rewind(void* p) UTILS_RESTRICT noexcept { - assert_invariant(p >= mBegin && p < end()); + assert(p >= mBegin && p < end()); set_current(p); } @@ -194,7 +193,7 @@ class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocat } ~LinearAllocatorWithFallback() noexcept { - LinearAllocatorWithFallback::reset(); + reset(); } void* alloc(size_t size, size_t alignment = alignof(std::max_align_t)); @@ -204,7 +203,7 @@ class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocat } void rewind(void* p) noexcept { - if (p >= LinearAllocator::base() && p < LinearAllocator::end()) { + if (p >= base() && p < end()) { LinearAllocator::rewind(p); } } @@ -214,7 +213,7 @@ class LinearAllocatorWithFallback : private LinearAllocator, private HeapAllocat void free(void*, size_t) noexcept { } bool isHeapAllocation(void* p) const noexcept { - return p < LinearAllocator::base() || p >= LinearAllocator::end(); + return p < base() || p >= end(); } }; @@ -233,15 +232,16 @@ class FreeList { Node* const head = mHead; mHead = head ? head->next : nullptr; // this could indicate a use after free - assert_invariant(!mHead || mHead >= mBegin && mHead < mEnd); + assert(!mHead || mHead >= mBegin && mHead < mEnd); return head; } void push(void* p) noexcept { - assert_invariant(p); - assert_invariant(p >= mBegin && p < mEnd); - // TODO: assert this is one of our pointer (i.e.: it's address match one of ours) - Node* const head = static_cast(p); + assert(p); + assert(p >= mBegin && p < mEnd); + // we use placement-new to properly manage the lifetime + // this is noop already under O1 + Node* const head = new (p) Node; head->next = mHead; mHead = head; } @@ -278,41 +278,50 @@ class AtomicFreeList { void* pop() noexcept { Node* const pStorage = mStorage; - HeadPtr currentHead = mHead.load(); + HeadPtr currentHead = mHead.load(std::memory_order_relaxed); while (currentHead.offset >= 0) { // The value of "pNext" we load here might already contain application data if another // thread raced ahead of us. But in that case, the computed "newHead" will be discarded - // since compare_exchange_weak fails. Then this thread will loop with the updated + // since compare_exchange_weak() fails. Then this thread will loop with the updated // value of currentHead, and try again. - Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed); + // TSAN complains if we don't use a local variable here. + Node const node = pStorage[currentHead.offset]; + Node const* const pNext = node.next; const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 }; - // In the rare case that the other thread that raced ahead of us already returned the - // same mHead we just loaded, but it now has a different "next" value, the tag field will not - // match, and compare_exchange_weak will fail and prevent that particular race condition. - if (mHead.compare_exchange_weak(currentHead, newHead)) { + // In the rare case that the other thread that raced ahead of us already returned the + // same mHead we just loaded, but it now has a different "next" value, the tag field + // will not match, and compare_exchange_weak() will fail and prevent that particular + // race condition. + // acquire: no read/write can be reordered before this + if (mHead.compare_exchange_weak(currentHead, newHead, + std::memory_order_acquire, std::memory_order_relaxed)) { // This assert needs to occur after we have validated that there was no race condition // Otherwise, next might already contain application data, if another thread // raced ahead of us after we loaded mHead, but before we loaded mHead->next. - assert_invariant(!pNext || pNext >= pStorage); + assert(!pNext || pNext >= pStorage); break; } } void* p = (currentHead.offset >= 0) ? (pStorage + currentHead.offset) : nullptr; - assert_invariant(!p || p >= pStorage); + assert(!p || p >= pStorage); return p; } void push(void* p) noexcept { Node* const storage = mStorage; - assert_invariant(p && p >= storage); - Node* const node = static_cast(p); - HeadPtr currentHead = mHead.load(); + assert(p && p >= storage); + // we use placement-new to properly manage the lifetime + // this is noop already under O1 + Node* const node = new (p) Node; + HeadPtr currentHead = mHead.load(std::memory_order_relaxed); HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 }; do { newHead.tag = currentHead.tag + 1; - Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; - node->next.store(n, std::memory_order_relaxed); - } while(!mHead.compare_exchange_weak(currentHead, newHead)); + Node* const pNext = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; + node->next = pNext; // could be a race with pop, corrected by CAS + } while(!mHead.compare_exchange_weak(currentHead, newHead, + std::memory_order_release, std::memory_order_relaxed)); + // release: no read/write can be reordered after this } void* getFirst() noexcept { @@ -320,10 +329,7 @@ class AtomicFreeList { } struct Node { - // This should be a regular (non-atomic) pointer, but this causes TSAN to complain - // about a data-race that exists but is benin. We always use this atomic<> in - // relaxed mode. - // The data race TSAN complains about is when a pop() is interrupted by a + // There is a benign data race when a pop() is interrupted by a // pop() + push() just after mHead->next is read -- it appears as though it is written // without synchronization (by the push), however in that case, the pop's CAS will fail // and things will auto-correct. @@ -346,7 +352,7 @@ class AtomicFreeList { // | // CAS, tag++ // - std::atomic next; + Node* next = nullptr; }; private: @@ -378,9 +384,9 @@ class PoolAllocator { // our allocator concept void* alloc(size_t size = ELEMENT_SIZE, size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept { - assert_invariant(size <= ELEMENT_SIZE); - assert_invariant(alignment <= ALIGNMENT); - assert_invariant(offset == OFFSET); + assert(size <= ELEMENT_SIZE); + assert(alignment <= ALIGNMENT); + assert(offset == OFFSET); return mFreeList.pop(); } @@ -458,7 +464,7 @@ class PoolAllocatorWithFallback : if (UTILS_UNLIKELY(!p)) { p = HeapAllocator::alloc(size, alignment); } - assert_invariant(p); + assert(p); return p; } @@ -714,13 +720,13 @@ class Arena { // trivially destructible, since free() won't call the destructor and this is allocating // an array. template ::value>::type> + typename = std::enable_if_t>> T* alloc(size_t count, size_t alignment, size_t extra) noexcept { return (T*)alloc(count * sizeof(T), alignment, extra); } template ::value>::type> + typename = std::enable_if_t>> T* alloc(size_t count, size_t alignment = alignof(T)) noexcept { return (T*)alloc(count * sizeof(T), alignment); } @@ -876,7 +882,7 @@ class ArenaScope { template T* make(ARGS&& ... args) noexcept { T* o = nullptr; - if (std::is_trivially_destructible::value) { + if (std::is_trivially_destructible_v) { o = mArena.template make(std::forward(args)...); } else { void* const p = (Finalizer*)mArena.alloc(sizeof(T), ALIGN, sizeof(Finalizer)); @@ -939,7 +945,7 @@ class STLAllocator { TYPE* allocate(std::size_t n) { auto p = static_cast(mArena.alloc(n * sizeof(TYPE), alignof(TYPE))); - assert_invariant(p); + assert(p); return p; } diff --git a/package/ios/libs/filament/include/utils/BitmaskEnum.h b/package/ios/libs/filament/include/utils/BitmaskEnum.h index 17f94d21..ae412204 100644 --- a/package/ios/libs/filament/include/utils/BitmaskEnum.h +++ b/package/ios/libs/filament/include/utils/BitmaskEnum.h @@ -41,32 +41,32 @@ size_t count(); // ------------------------------------------------------------------------------------------------ template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr int operator+(Enum value) noexcept { return int(value); } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator==(Enum lhs, size_t rhs) noexcept { using underlying_t = std::underlying_type_t; return underlying_t(lhs) == rhs; } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator==(size_t lhs, Enum rhs) noexcept { return rhs == lhs; } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator!=(Enum lhs, size_t rhs) noexcept { return !(rhs == lhs); } template::value && utils::EnableIntegerOperators::value, int> = 0> + std::is_enum_v && utils::EnableIntegerOperators::value, int> = 0> inline constexpr bool operator!=(size_t lhs, Enum rhs) noexcept { return rhs != lhs; } @@ -74,66 +74,66 @@ inline constexpr bool operator!=(size_t lhs, Enum rhs) noexcept { // ------------------------------------------------------------------------------------------------ template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr bool operator!(Enum rhs) noexcept { using underlying = std::underlying_type_t; return underlying(rhs) == 0; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator~(Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(~underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(underlying(lhs) | underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(underlying(lhs) & underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept { using underlying = std::underlying_type_t; return Enum(underlying(lhs) ^ underlying(rhs)); } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs | rhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs & rhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept { return lhs = lhs ^ rhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr bool none(Enum lhs) noexcept { return !lhs; } template::value && utils::EnableBitMaskOperators::value, int> = 0> + std::is_enum_v && utils::EnableBitMaskOperators::value, int> = 0> inline constexpr bool any(Enum lhs) noexcept { return !none(lhs); } diff --git a/package/ios/libs/filament/include/utils/CString.h b/package/ios/libs/filament/include/utils/CString.h index d5b76951..935845f2 100644 --- a/package/ios/libs/filament/include/utils/CString.h +++ b/package/ios/libs/filament/include/utils/CString.h @@ -20,6 +20,11 @@ // NOTE: this header should not include STL headers #include +#include + +#include +#include +#include #include #include @@ -28,27 +33,22 @@ #include 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 const c = *cstr++) { - hash = (hash * 33u) ^ size_t(c); - } - return hash; - } -}; +namespace io { +class ostream; +} template using StringLiteral = const char[N]; - // ------------------------------------------------------------------------------------------------ class UTILS_PUBLIC CString { + static constexpr bool TRACK_AND_LOG_ALLOCATIONS = false; + + template + static constexpr bool is_char_pointer_v = + std::is_pointer_v && std::is_same_v>>; + public: using value_type = char; using size_type = uint32_t; @@ -60,7 +60,9 @@ class UTILS_PUBLIC CString { using iterator = value_type*; using const_iterator = const value_type*; - CString() noexcept {} // NOLINT(modernize-use-equals-default), Ubuntu compiler bug + CString() noexcept { + track(true); + } // 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). @@ -71,22 +73,45 @@ class UTILS_PUBLIC CString { // inside the string. explicit CString(size_t length); + // Conversion from std::string_view + explicit CString(const std::string_view& str) + : CString(str.data(), str.size()) { + } + // Allocates memory and copies traditional C string content. Unlike the above constructor, this // does not allow embedded nulls. This is explicit because this operation is costly. - explicit CString(const char* cstr); + // This is a template to ensure it's not preferred over the string literal constructor below. + template>> + explicit CString(T cstr) : CString(cstr, cstr ? strlen(cstr) : 0) { + track(true); + } + // The string can't have NULs in it. template CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) : CString(other, N - 1) { + track(true); + } + + // This constructor can be used if the string has NULs in it. + template + CString(StringLiteral const& other, size_t const length) noexcept + : CString(other, length) { + track(true); + } + + CString(StaticString const& other) noexcept // NOLINT(*-explicit-constructor) + : CString(other.c_str(), other.length()) { + track(true); } CString(const CString& rhs); CString(CString&& rhs) noexcept { + track(true); this->swap(rhs); } - CString& operator=(const CString& rhs); CString& operator=(CString&& rhs) noexcept { @@ -94,11 +119,7 @@ class UTILS_PUBLIC CString { return *this; } - ~CString() noexcept { - if (mData) { - free(mData - 1); - } - } + ~CString() noexcept; void swap(CString& other) noexcept { // don't use std::swap(), we don't want an STL dependency in this file @@ -107,8 +128,8 @@ class UTILS_PUBLIC CString { other.mCStr = temp; } - const_pointer c_str() const noexcept { return mCStr; } pointer c_str() noexcept { return mCStr; } + const_pointer c_str() const noexcept { return const_cast(this)->c_str(); } 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(); } @@ -116,32 +137,169 @@ class UTILS_PUBLIC CString { size_type length() const noexcept { return size(); } bool empty() const noexcept { return size() == 0; } - iterator begin() noexcept { return mCStr; } + iterator begin() noexcept { return c_str(); } 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); } + // replace + template + CString& replace(size_type const pos, size_type const len, const StringLiteral& str) & noexcept { + return replace(pos, len, str, N - 1); + } + + CString& replace(size_type const pos, size_type const len, const CString& str) & noexcept { + return replace(pos, len, str.c_str_safe(), str.size()); + } + + template >> + CString& replace(size_type pos, size_type len, T str) & noexcept { + if (str) { + return replace(pos, len, str, strlen(str)); + } + return replace(pos, len, "", 0); + } + + template + CString&& replace(size_type pos, size_type len, const StringLiteral& str) && noexcept { + this->replace(pos, len, str); + return std::move(*this); + } + + CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept { + this->replace(pos, len, str); + return std::move(*this); + } + + template >> + CString&& replace(size_type pos, size_type len, T str) && noexcept { + this->replace(pos, len, str); + return std::move(*this); + } + + + // insert + CString& insert(size_type const pos, char const c) & noexcept { + const char s[1] = { c }; + return replace(pos, 0, s, 1); + } + + template + CString& insert(size_type const pos, const StringLiteral& str) & noexcept { + return replace(pos, 0, str, N - 1); + } + + CString& insert(size_type const pos, const CString& str) & noexcept { + return replace(pos, 0, str.c_str_safe(), str.size()); + } + + template >> + CString& insert(size_type pos, T str) & noexcept { + if (str) { + return replace(pos, 0, str, strlen(str)); + } + return *this; + } + + CString&& insert(size_type const pos, char const c) && noexcept { + this->insert(pos, c); + return std::move(*this); + } + + template + CString&& insert(size_type pos, const StringLiteral& str) && noexcept { + this->insert(pos, str); + return std::move(*this); + } + + CString&& insert(size_type const pos, const CString& str) && noexcept { + this->insert(pos, str); + return std::move(*this); + } + + template >> + CString&& insert(size_type pos, T str) && noexcept { + this->insert(pos, str); + return std::move(*this); + } + + + // append + CString& append(char const c) & noexcept { + return insert(length(), c); + } + + template + CString& append(const StringLiteral& str) & noexcept { + return insert(length(), str); + } + + CString& append(const CString& str) & noexcept { + return insert(length(), str); + } + + template>> + CString& append(T str) & noexcept { + return insert(length(), str); + } + + CString&& append(char const c) && noexcept { + this->append(c); + return std::move(*this); + } + + template + CString&& append(const StringLiteral& str) && noexcept { + this->append(str); + return std::move(*this); + } + + CString&& append(const CString& str) && noexcept { + this->append(str); + return std::move(*this); + } + + template>> + CString&& append(T str) && noexcept { + this->append(str); + return std::move(*this); + } + + // operator+= + CString& operator+=(char const c) & noexcept { + return append(c); + } + + CString& operator+=(const CString& str) & noexcept { + return append(str); + } + template + CString& operator+=(const StringLiteral& str) & noexcept { + return append(str); + } + template >> + CString& operator+=(T str) & noexcept { + return append(str); + } - const_reference operator[](size_type pos) const noexcept { + const_reference operator[](size_type const pos) const noexcept { assert(pos < size()); return begin()[pos]; } - reference operator[](size_type pos) noexcept { + reference operator[](size_type const pos) noexcept { assert(pos < size()); return begin()[pos]; } - const_reference at(size_type pos) const noexcept { + const_reference at(size_type const pos) const noexcept { assert(pos < size()); return begin()[pos]; } - reference at(size_type pos) noexcept { + reference at(size_type const pos) noexcept { assert(pos < size()); return begin()[pos]; } @@ -167,20 +325,30 @@ class UTILS_PUBLIC CString { } // placement new declared as "throw" to avoid the compiler's null-check - inline void* operator new(size_t, void* ptr) { + void* operator new(size_t, void* ptr) { assert(ptr); return ptr; } - struct Hasher : private hashCStrings { - typedef CString argument_type; - typedef size_t result_type; - result_type operator()(const argument_type& s) const noexcept { - return hashCStrings::operator()(s.c_str()); - } - }; + // conversion to std::string_view + operator std::string_view() const noexcept { + return std::string_view{data(), size()}; + } private: + static void do_tracking(bool ctor); + static void track(bool ctor) { + if constexpr (TRACK_AND_LOG_ALLOCATIONS) { + do_tracking(ctor); + } + } + + CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept; + +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const CString& rhs); +#endif + struct Data { size_type length; }; @@ -191,17 +359,16 @@ class UTILS_PUBLIC CString { Data* mData; // Data is stored at mData[-1] }; + int compare(const std::string_view& rhs) const noexcept { + return std::string_view{data(), size()}.compare(rhs); + } + int compare(const CString& rhs) const noexcept { - size_type const lhs_size = size(); - size_type const rhs_size = rhs.size(); - if (lhs_size < rhs_size) return -1; - if (lhs_size > rhs_size) return 1; - return strncmp(data(), rhs.data(), size()); + return compare(std::string_view{rhs.data(), rhs.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())); + return lhs.compare(rhs) == 0; } friend bool operator!=(CString const& lhs, CString const& rhs) noexcept { return !(lhs == rhs); @@ -218,9 +385,125 @@ class UTILS_PUBLIC CString { friend bool operator<=(CString const& lhs, CString const& rhs) noexcept { return !(lhs > rhs); } + + friend bool operator==(CString const& lhs, std::string_view const& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator==(std::string_view const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(CString const& lhs, std::string_view const& rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator!=(std::string_view const& lhs, CString const& rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator<(CString const& lhs, std::string_view const& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator<(std::string_view const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(CString const& lhs, std::string_view const& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator>(std::string_view const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(CString const& lhs, std::string_view const& rhs) noexcept { + return !(lhs < rhs); + } + friend bool operator>=(std::string_view const& lhs, CString const& rhs) noexcept { + return !(lhs < rhs); + } + friend bool operator<=(CString const& lhs, std::string_view const& rhs) noexcept { + return !(lhs > rhs); + } + friend bool operator<=(std::string_view const& lhs, CString const& rhs) noexcept { + return !(lhs > rhs); + } }; -// implement this for your type for automatic conversion to CString. Failing to do so leads +// operator+ +inline CString operator+(CString lhs, const CString& rhs) { + lhs += rhs; + return lhs; +} + +inline CString operator+(CString lhs, const char* rhs) { + lhs += rhs; + return lhs; +} + +inline CString operator+(const char* lhs, CString rhs) { + rhs.insert(0, lhs); + return rhs; +} + +inline CString operator+(CString lhs, char const rhs) { + lhs += rhs; + return lhs; +} + +inline CString operator+(char const lhs, CString rhs) { + rhs.insert(0, lhs); + return rhs; +} + +// CString vs StringLiteral +template + bool operator==(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} == std::string_view{rhs, N - 1}; +} +template + bool operator!=(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} != std::string_view{rhs, N - 1}; +} +template + bool operator<(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} < std::string_view{rhs, N - 1}; +} +template + bool operator>(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} > std::string_view{rhs, N - 1}; +} +template + bool operator<=(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} <= std::string_view{rhs, N - 1}; +} +template + bool operator>=(CString const& lhs, const StringLiteral& rhs) noexcept { + return std::string_view{lhs.data(), lhs.size()} >= std::string_view{rhs, N - 1}; +} + +// StringLiteral vs CString +template + bool operator==(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} == std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator!=(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} != std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator<(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} < std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator>(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} > std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator<=(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} <= std::string_view{rhs.data(), rhs.size()}; +} +template + bool operator>=(const StringLiteral& lhs, CString const& rhs) noexcept { + return std::string_view{lhs, M - 1} >= std::string_view{rhs.data(), rhs.size()}; +} + + +// Implement this for your type for automatic conversion to CString. Failing to do so leads // to a compile-time failure. template CString to_string(T value) noexcept; @@ -244,9 +527,47 @@ class UTILS_PUBLIC FixedSizeString { pointer c_str() noexcept { return mData; } private: - value_type mData[N] = {0}; + value_type mData[N] = {}; }; } // namespace utils +// heterogeneous lookup support for associative containers +namespace std { + template <> + struct hash { + using is_transparent = void; // Enable heterogeneous lookup + + size_t operator()(const utils::CString& k) const noexcept { + return compute_hash(std::string_view(k)); + } + + template && + !std::is_same_v, utils::CString>>> + size_t operator()(const T& k) const noexcept { + return compute_hash(std::string_view(k)); + } + + private: + size_t compute_hash(std::string_view k) const noexcept { + size_t hash = 5381; + for (char const c : k) { + hash = (hash * 33u) ^ size_t(c); + } + return hash; + } + }; + + template<> + struct equal_to { + using is_transparent = void; // Enable heterogeneous lookup + + template + bool operator()(const T& lhs, const U& rhs) const { + return lhs == rhs; + } + }; +} + #endif // TNT_UTILS_CSTRING_H diff --git a/package/ios/libs/filament/include/utils/CallStack.h b/package/ios/libs/filament/include/utils/CallStack.h index 33ac0b50..f8366cfa 100644 --- a/package/ios/libs/filament/include/utils/CallStack.h +++ b/package/ios/libs/filament/include/utils/CallStack.h @@ -23,9 +23,11 @@ #include #include -#include namespace utils { +namespace io { +class ostream; +} /** * CallStack captures the current's thread call stack. @@ -68,10 +70,10 @@ class CallStack { intptr_t operator [](size_t index) const; /** Demangles a C++ type name */ - static utils::CString demangleTypeName(const char* mangled); + static CString demangleTypeName(const char* mangled); template - static utils::CString typeName() { + static CString typeName() { #if UTILS_HAS_RTTI return demangleTypeName(typeid(T).name()); #else @@ -84,34 +86,34 @@ class CallStack { * 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); + friend io::ostream& operator <<(io::ostream& stream, const CallStack& callstack); bool operator <(const CallStack& rhs) const; - inline bool operator >(const CallStack& rhs) const { + bool operator >(const CallStack& rhs) const { return rhs < *this; } - inline bool operator !=(const CallStack& rhs) const { + bool operator !=(const CallStack& rhs) const { return *this < rhs || rhs < *this; } - inline bool operator >=(const CallStack& rhs) const { + bool operator >=(const CallStack& rhs) const { return !operator <(rhs); } - inline bool operator <=(const CallStack& rhs) const { + bool operator <=(const CallStack& rhs) const { return !operator >(rhs); } - inline bool operator ==(const CallStack& rhs) const { + 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 CString demangle(const char* mangled); static constexpr size_t NUM_FRAMES = 20; diff --git a/package/ios/libs/filament/include/utils/FixedCapacityVector.h b/package/ios/libs/filament/include/utils/FixedCapacityVector.h index f3990124..fd920876 100644 --- a/package/ios/libs/filament/include/utils/FixedCapacityVector.h +++ b/package/ios/libs/filament/include/utils/FixedCapacityVector.h @@ -17,10 +17,11 @@ #ifndef TNT_UTILS_FIXEDCAPACITYVECTOR_H #define TNT_UTILS_FIXEDCAPACITYVECTOR_H +#include #include #include -#include +#include #include #include #include @@ -40,6 +41,11 @@ namespace utils { +class UTILS_PUBLIC FixedCapacityVectorBase { +protected: + UTILS_NORETURN static void capacityCheckFailed(size_t capacity, size_t size); +}; + /** * 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() @@ -56,7 +62,7 @@ namespace utils { * the optional value argument, e.g. FixedCapacityVector(4, 0) or foo.resize(4, 0). */ template, bool CapacityCheck = true> -class UTILS_PUBLIC FixedCapacityVector { +class UTILS_PUBLIC FixedCapacityVector : protected FixedCapacityVectorBase { public: using allocator_type = A; using value_type = T; @@ -84,7 +90,7 @@ class UTILS_PUBLIC FixedCapacityVector { FixedCapacityVector() = default; explicit FixedCapacityVector(const allocator_type& allocator) noexcept - : mCapacityAllocator({}, allocator) { + : mCapacityAllocator(0, allocator) { } explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type()) @@ -122,6 +128,14 @@ class UTILS_PUBLIC FixedCapacityVector { this->swap(rhs); } + FixedCapacityVector(utils::Slice rhs, + const allocator_type& alloc = allocator_type()) + : mSize(rhs.size()), + mCapacityAllocator(mSize, alloc) { + mData = this->allocator().allocate(this->capacity()); + std::uninitialized_copy(rhs.cbegin(), rhs.cend(), begin()); + } + ~FixedCapacityVector() noexcept { destroy(begin(), end()); allocator().deallocate(data(), capacity()); @@ -140,6 +154,29 @@ class UTILS_PUBLIC FixedCapacityVector { return *this; } + bool operator==(const FixedCapacityVector& rhs) const noexcept { + if (this == &rhs) { + return true; + } + if (size() != rhs.size()) { + return false; + } + return std::equal(begin(), end(), rhs.begin()); + } + + Slice as_slice() noexcept { + return { begin(), end() }; + } + + Slice as_slice() const noexcept { + return { cbegin(), cend() }; + } + + template> + inline size_t hash() const noexcept { + return as_slice().template hash(); + } + allocator_type get_allocator() const noexcept { return mCapacityAllocator.second(); } @@ -266,7 +303,7 @@ class UTILS_PUBLIC FixedCapacityVector { mSize = 0; } - void resize(size_type count) { + void resize(size_type const count) { assertCapacityForSize(count); if constexpr(std::is_trivially_constructible_v && std::is_trivially_destructible_v) { @@ -277,12 +314,12 @@ class UTILS_PUBLIC FixedCapacityVector { } } - void resize(size_type count, const_reference v) { + void resize(size_type const count, const_reference v) { assertCapacityForSize(count); resize_non_trivial(count, v); } - void swap(FixedCapacityVector& other) { + void swap(FixedCapacityVector& other) noexcept { using std::swap; swap(mData, other.mData); swap(mSize, other.mSize); @@ -299,6 +336,16 @@ class UTILS_PUBLIC FixedCapacityVector { } } + UTILS_NOINLINE + void shrink_to_fit() { + if (size() < capacity()) { + FixedCapacityVector t(construct_with_capacity, size(), allocator()); + t.mSize = size(); + std::uninitialized_move(begin(), end(), t.begin()); + this->swap(t); + } + } + private: enum construct_with_capacity_tag{ construct_with_capacity }; @@ -316,16 +363,16 @@ class UTILS_PUBLIC FixedCapacityVector { return mCapacityAllocator.second(); } - iterator assertCapacityForSize(size_type s) { + iterator assertCapacityForSize(size_type const 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()); + if (UTILS_VERY_UNLIKELY(capacity() < s)) { + capacityCheckFailed(capacity(), s); + } } return end(); } - inline void construct(iterator first, iterator last) noexcept { + void construct(iterator const first, iterator const last) noexcept { // we check for triviality here so that the implementation could be non-inline if constexpr(!std::is_trivially_constructible_v) { construct_non_trivial(first, last); @@ -348,7 +395,7 @@ class UTILS_PUBLIC FixedCapacityVector { } - inline void destroy(iterator first, iterator last) noexcept { + void destroy(iterator const first, iterator const last) noexcept { // we check for triviality here so that the implementation could be non-inline if constexpr(!std::is_trivially_destructible_v) { destroy_non_trivial(first, last); @@ -409,7 +456,7 @@ class UTILS_PUBLIC FixedCapacityVector { explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { } SizeTypeWrapper& operator=(TYPE rhs) noexcept { value = rhs; return *this; } SizeTypeWrapper& operator=(SizeTypeWrapper& rhs) noexcept = delete; - operator TYPE() const noexcept { return value; } + operator TYPE() const noexcept { return value; } // NOLINT(*-explicit-constructor) }; pointer mData{}; @@ -419,4 +466,13 @@ class UTILS_PUBLIC FixedCapacityVector { } // namespace utils +namespace std { +template +struct hash> { + inline size_t operator()(utils::FixedCapacityVector const& lhs) const noexcept { + return lhs.hash(); + } +}; +} // namespace std + #endif // TNT_UTILS_FIXEDCAPACITYVECTOR_H diff --git a/package/ios/libs/filament/include/utils/Hash.h b/package/ios/libs/filament/include/utils/Hash.h new file mode 100644 index 00000000..f1ff3aff --- /dev/null +++ b/package/ios/libs/filament/include/utils/Hash.h @@ -0,0 +1,149 @@ +/* + * 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 // for std::hash +#include +#include +#include + +#include +#include + +namespace utils::hash { + +// The standard CRC-32 polynomial (used in Ethernet, ZIP, PNG, etc.) +static constexpr uint32_t CRC32_POLYNOMIAL = 0xEDB88320; + +// Generates the lookup table for the CRC-32 algorithm. +inline void crc32GenerateTable(std::vector& table) { + if (table.size() != 256) { + table.resize(256); + } + + for (uint32_t i = 0; i < 256; ++i) { + uint32_t crc = i; + for (int j = 0; j < 8; ++j) { + if (crc & 1) { + crc = (crc >> 1) ^ CRC32_POLYNOMIAL; + } else { + crc = crc >> 1; + } + } + table[i] = crc; + } +} + +// Updates the CRC-32 value with a new chunk of data. This function can be called multiple times to +// process a large buffer in smaller, separate parts. +// @param previous_crc The CRC value returned from the previous call to `crc32Update`. +// @param data A pointer to the data buffer for this chunk. +// @param length The length of the data buffer in bytes. +// @return The new CRC value. +inline uint32_t crc32Update(uint32_t previous_crc, const void* data, size_t length, const std::vector& table) { + uint32_t crc = ~previous_crc; + const uint8_t* current = static_cast(data); + for (size_t i = 0; i < length; ++i) { + // The core CRC-32 calculation step. + crc = (crc >> 8) ^ table[(crc ^ current[i]) & 0xFF]; + } + return ~crc; +} + +inline size_t combine(size_t lhs, size_t rhs) noexcept { + std::pair const p{ lhs, rhs }; + return std::hash{}({ (char*)&p, sizeof(p) }); +} + +// Hash function that takes an arbitrary swath of word-aligned data. +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; +} + +// The hash yields the same result for a given byte sequence regardless of alignment. +inline uint32_t murmurSlow(const uint8_t* key, size_t byteCount, uint32_t seed) noexcept { + const size_t wordCount = (byteCount + 3) / 4; + const uint8_t* const last = key + byteCount; + + // The remainder is identical to murmur3() except an inner loop safely "reads" an entire word. + uint32_t h = seed; + size_t wc = wordCount; + do { + uint32_t k = 0; + for (int i = 0; i < 4 && key < last; ++i, ++key) { + k >>= 8; + k |= uint32_t(*key) << 24; + } + k *= 0xcc9e2d51u; + k = (k << 15u) | (k >> 17u); + k *= 0x1b873593u; + h ^= k; + h = (h << 13u) | (h >> 19u); + h = (h * 5u) + 0xe6546b64u; + } while (--wc); + h ^= wordCount; + h ^= h >> 16u; + h *= 0x85ebca6bu; + h ^= h >> 13u; + h *= 0xc2b2ae35u; + h ^= h >> 16u; + return h; +} + +template +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 +inline void combine(size_t& seed, const T& v) noexcept { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u); +} + +// combines two hashes together, faster but less good +template +inline void combine_fast(size_t& seed, const T& v) noexcept { + std::hash hasher; + seed ^= hasher(v) << 1u; +} + +} // namespace utils::hash + +#endif // TNT_UTILS_HASH_H diff --git a/package/ios/libs/filament/include/utils/ImmutableCString.h b/package/ios/libs/filament/include/utils/ImmutableCString.h new file mode 100644 index 00000000..48fbf430 --- /dev/null +++ b/package/ios/libs/filament/include/utils/ImmutableCString.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2025 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_IMMUTABLECSTRING_H +#define TNT_UTILS_IMMUTABLECSTRING_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace utils { + + +class UTILS_PUBLIC ImmutableCString { + static constexpr bool TRACK_AND_LOG_ALLOCATIONS = false; + + template + static constexpr bool is_char_pointer_v = + std::is_pointer_v && std::is_same_v>>; + +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*; + + ImmutableCString() noexcept { + track(true, mIsStatic); + } + + // The string can't have NULs in it. + template + ImmutableCString(const char (&str)[N]) noexcept : mData(str), mSize(N - 1) { // NOLINT(*-explicit-constructor) + track(true, mIsStatic); + } + + // This constructor can be used if the string has NULs in it. + template + ImmutableCString(const char (&str)[N], size_t const length) noexcept + : mData(str), mSize(length) { + track(true, mIsStatic); + } + + template>> + explicit ImmutableCString(T cstr) { + if (cstr) { + initializeFrom(cstr, strlen(cstr)); + } + track(true, mIsStatic); + } + + ImmutableCString(const char* cstr, size_t const length) { + initializeFrom(cstr, length); + track(true, mIsStatic); + } + + ImmutableCString(StaticString const& str) // NOLINT(*-explicit-constructor) + : mData(str.data()), mSize(str.size()) { + track(true, mIsStatic); + } + + ImmutableCString(const ImmutableCString& other) { + if (other.mIsStatic) { + mIsStatic = other.mIsStatic; + mSize = other.mSize; + mData = other.mData; + } else { + initializeFrom(other.mData, other.mSize); + } + track(true, mIsStatic); + } + + ImmutableCString(ImmutableCString&& other) noexcept { + track(true, mIsStatic); + this->swap(other); + } + + ImmutableCString& operator=(const ImmutableCString& other); + + ImmutableCString& operator=(ImmutableCString&& other) noexcept; + + ~ImmutableCString() { + track(false, mIsStatic); + if (!mIsStatic) { + free(const_cast(mData)); + } + } + + bool isStatic() const noexcept { return mIsStatic; } + bool isDynamic() const noexcept { return !mIsStatic; } + + const_pointer c_str_safe() const noexcept { return mData; } + const_pointer c_str() const noexcept { return mData; } + const_pointer data() const noexcept { return mData; } + size_type size() const noexcept { return mSize; } + size_type length() const noexcept { return mSize; } + bool empty() const noexcept { return mSize == 0; } + + const_iterator begin() const noexcept { return mData; } + const_iterator end() const noexcept { return mData + mSize; } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_reference operator[](size_type const pos) const noexcept { + assert(pos < mSize); + return mData[pos]; + } + + const_reference at(size_type const pos) const noexcept { + assert(pos < mSize); + return mData[pos]; + } + + const_reference front() const noexcept { + assert(mSize > 0); + return mData[0]; + } + + const_reference back() const noexcept { + assert(mSize > 0); + return mData[mSize - 1]; + } + + void swap(ImmutableCString& other) noexcept { + std::swap(mData, other.mData); + std::swap(mSize, other.mSize); + std::swap(mIsStatic, other.mIsStatic); + } + +private: + static void do_tracking(bool ctor, bool is_static); + static void track(bool ctor, bool is_static) { + if constexpr (TRACK_AND_LOG_ALLOCATIONS) { + do_tracking(ctor, is_static); + } + } + +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs); +#endif + + void initializeFrom(const char* cstr, size_t length); + + int compare(const ImmutableCString& rhs) const noexcept { + return std::string_view{ mData, mSize }.compare({ rhs.mData, rhs.mSize }); + } + + char const* mData = ""; + uint32_t mSize = 0; + bool mIsStatic = true; + + friend bool operator==(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) == 0; + } + friend bool operator!=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) != 0; + } + friend bool operator<(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator<=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) <= 0; + } + friend bool operator>=(const ImmutableCString& lhs, const ImmutableCString& rhs) noexcept { + return lhs.compare(rhs) >= 0; + } +}; + +static_assert(sizeof(ImmutableCString) <= 16, "ImmutableCString should be 16 bytes or less"); + +} // namespace utils + +#endif //TNT_UTILS_IMMUTABLECSTRING_H diff --git a/package/ios/libs/filament/include/utils/InternPool.h b/package/ios/libs/filament/include/utils/InternPool.h new file mode 100644 index 00000000..b9ccccbd --- /dev/null +++ b/package/ios/libs/filament/include/utils/InternPool.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2025 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_INTERNPOOL_H +#define TNT_UTILS_INTERNPOOL_H + +#include +#include +#include +#include +#include + +#include + +namespace utils { + +/** A reference-counted intern pool of slices of T. */ +template> +class InternPool { + struct HashSlice { + inline size_t operator()(Slice const& slice) const noexcept { + return slice.template hash(); + } + }; + + struct Entry { + uint32_t referenceCount; + FixedCapacityVector value; + }; + + using Map = tsl::robin_map, Entry, HashSlice>; + + static constexpr const char* UTILS_NONNULL MISSING_ENTRY_ERROR_STRING = + "InternPool is missing entry"; + +public: + InternPool() = default; + InternPool(InternPool const& rhs) = delete; + InternPool& operator=(InternPool const& rhs) = delete; + InternPool(InternPool&& rhs) = default; + InternPool& operator=(InternPool&& rhs) = default; + + /** Acquire an interned copy of value. */ + Slice acquire(Slice slice, size_t hash) noexcept { + if (slice.empty()) { + return { nullptr, nullptr }; + } + auto it = mMap.find(slice, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return it.key(); + } + FixedCapacityVector value(slice); + // TODO: how to use above computed hash here? + return mMap.insert({ value.as_slice(), Entry{ 1, std::move(value) } }).first.key(); + } + + inline Slice acquire(Slice slice) noexcept { + return acquire(slice, HashSlice{}(slice)); + } + + Slice acquire(FixedCapacityVector&& value, size_t hash) noexcept { + if (value.empty()) { + return { nullptr, nullptr }; + } + Slice slice = value.as_slice(); + auto it = mMap.find(slice, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return it.key(); + } + // TODO: how to use above computed hash here? + return mMap.insert({ slice, Entry{ 1, std::move(value) } }).first.key(); + } + + inline Slice acquire(FixedCapacityVector&& value) noexcept { + size_t hash = HashSlice{}(value.as_slice()); + return acquire(std::move(value), hash); + } + + inline Slice acquire(FixedCapacityVector const& value, size_t hash) noexcept { + return acquire(value.as_slice(), hash); + } + + inline Slice acquire(FixedCapacityVector const& value) noexcept { + Slice slice = value.as_slice(); + return acquire(slice, HashSlice{}(slice)); + } + + /** Release interned value. */ + void release(Slice slice, size_t hash) { + if (slice.empty()) { + return; + } + auto it = mMap.find(slice, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + if (--it.value().referenceCount == 0) { + // TODO: change to erase_fast + mMap.erase(it); + } + } + + inline void release(Slice slice) noexcept { + return release(slice, HashSlice{}(slice)); + } + + inline void release(FixedCapacityVector const& value, size_t hash) noexcept { + return release(value.as_slice(), hash); + } + + inline void release(FixedCapacityVector const& value) noexcept { + Slice slice = value.as_slice(); + return release(slice, HashSlice{}(slice)); + } + + /** Returns true if the pool is empty. */ + inline bool empty() const noexcept { return mMap.empty(); } + + /** Returns hash of value. */ + static size_t hash(Slice slice) noexcept { + return HashSlice{}(slice); + } + + static size_t hash(FixedCapacityVector const& value) noexcept { + return HashSlice{}(value.as_slice()); + } + +private: + Map mMap; +}; + +} // namespace utils + +#endif // TNT_UTILS_INTERNPOOL_H diff --git a/package/ios/libs/filament/include/utils/Invocable.h b/package/ios/libs/filament/include/utils/Invocable.h index 49b43071..08d42968 100644 --- a/package/ios/libs/filament/include/utils/Invocable.h +++ b/package/ios/libs/filament/include/utils/Invocable.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef TNT_UTILS_INVOKABLE_H -#define TNT_UTILS_INVOKABLE_H +#ifndef TNT_UTILS_INVOCABLE_H +#define TNT_UTILS_INVOCABLE_H #include #include @@ -23,6 +23,9 @@ #include namespace utils { +namespace io { +class ostream; +} /* * Invocable is a move-only general purpose function wrapper. Instances can @@ -48,15 +51,21 @@ template using EnableIfFnMatchesInvocable = std::enable_if_t; #endif +class InvocableBase { +protected: + static io::ostream& printInvocable(io::ostream& out, const char* name); +}; + template class Invocable; template -class Invocable { +class Invocable : protected InvocableBase { public: // Creates an Invocable that does not contain a functor. // Will evaluate to false. Invocable() = default; + Invocable(std::nullptr_t) noexcept {} ~Invocable() noexcept; @@ -69,6 +78,7 @@ class Invocable { Invocable& operator=(const Invocable&) = delete; Invocable& operator=(Invocable&& rhs) noexcept; + Invocable& operator=(std::nullptr_t) noexcept; // Invokes the invocable with the args passed in. // If the Invocable is empty, this will assert. @@ -81,6 +91,11 @@ class Invocable { explicit operator bool() const noexcept; private: +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const Invocable&) { + return printInvocable(out, "Invocable<>"); // TODO: is there a way to do better here? + } +#endif void* mInvocable = nullptr; void (*mDeleter)(void*) = nullptr; R (* mInvoker)(void*, Args...) = nullptr; @@ -128,6 +143,17 @@ Invocable& Invocable::operator=(Invocable&& rhs) noexcep return *this; } +template +Invocable& Invocable::operator=(std::nullptr_t) noexcept { + if (mDeleter) { + mDeleter(mInvocable); + } + mInvocable = nullptr; + mDeleter = nullptr; + mInvoker = nullptr; + return *this; +} + template template R Invocable::operator()(OperatorArgs&& ... args) { @@ -149,4 +175,4 @@ Invocable::operator bool() const noexcept { } // namespace utils -#endif // TNT_UTILS_INVOKABLE_H +#endif // TNT_UTILS_INVOCABLE_H diff --git a/package/ios/libs/filament/include/utils/Logger.h b/package/ios/libs/filament/include/utils/Logger.h new file mode 100644 index 00000000..b385c85b --- /dev/null +++ b/package/ios/libs/filament/include/utils/Logger.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2025 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_LOGGER_H +#define TNT_UTILS_LOGGER_H + +// Logger.h provides a subset of the Abseil logging API, offering the following macros: + +// **LOG(severity)**: Logs a message at the specified severity level. +// **DLOG(severity)**: Logs a message at the specified severity level only in debug builds. + +// Supported `severity` levels are: +// * `INFO` +// * `WARNING` +// * `ERROR` +// * `FATAL` + +// For programmatic control over logging severity, use the `LEVEL` macro: + +// **LOG(LEVEL(expression))**: Logs a message at a severity level determined by the `expression`. +// The `expression` must return a `utils::LogSeverity` value, which is equivalent to +// `absl::LogSeverity`. + +#if defined(FILAMENT_USE_ABSEIL_LOGGING) + +#include +#include + +namespace utils { +using absl::LogSeverity; +} + +#else + +#include +#include + +namespace utils { + +enum class LogSeverity : int { + kInfo = 0, + kWarning = 1, + kError = 2, + kFatal = 3, +}; + +template +class LogLine { +public: + explicit LogLine(Stream& stream) + : mStream(stream) {} + + LogLine(const LogLine&) = delete; + LogLine(LogLine&&) = delete; + LogLine& operator=(const LogLine&) = delete; + LogLine& operator=(LogLine&&) = delete; + + ~LogLine() noexcept { mStream << io::endl; } + + template + LogLine& operator<<(T&& value) { + mStream << std::forward(value); + return *this; + } + +private: + Stream& mStream; +}; + +static inline io::ostream& getLogStream(LogSeverity severity) { + switch (severity) { + case LogSeverity::kInfo: + return slog.d; + case LogSeverity::kWarning: + return slog.w; + case LogSeverity::kError: + return slog.e; + case LogSeverity::kFatal: + return slog.e; + default: + return slog.d; + } +} + +struct NoopStream final { + template + NoopStream& operator<<(const T&) { + return *this; + } +}; + +#define LOG(severity) LOG_IMPL_##severity + +#ifndef NDEBUG +#define DLOG(severity) DLOG_IMPL_##severity +#else +#define DLOG(severity) utils::NoopStream() +#endif + +#define DLOG_IMPL_INFO utils::LogLine(utils::slog.d) +#define DLOG_IMPL_WARNING utils::LogLine(utils::slog.d) +#define DLOG_IMPL_ERROR utils::LogLine(utils::slog.d) +#define DLOG_IMPL_LEVEL(severity) utils::LogLine(((void) severity, utils::slog.d)) + +#define LOG_IMPL_INFO utils::LogLine(utils::slog.i) +#define LOG_IMPL_WARNING utils::LogLine(utils::slog.w) +#define LOG_IMPL_ERROR utils::LogLine(utils::slog.e) +#define LOG_IMPL_LEVEL(severity) utils::LogLine(getLogStream(severity)) + +}// namespace utils +#endif + +#endif// TNT_UTILS_LOGGER_H diff --git a/package/ios/libs/filament/include/utils/MonotonicRingMap.h b/package/ios/libs/filament/include/utils/MonotonicRingMap.h new file mode 100644 index 00000000..01ead770 --- /dev/null +++ b/package/ios/libs/filament/include/utils/MonotonicRingMap.h @@ -0,0 +1,157 @@ +/* +* Copyright (C) 2025 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_MONOTONICRINGMAP_H +#define TNT_UTILS_MONOTONICRINGMAP_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace utils { + +/** + * A map-like container with a fixed capacity and monotonically increasing keys. + * When the map is full, inserting a new element overwrites the oldest one. + * This container doesn't allocate any memory on the heap. + * Lookups are O(log N). + */ +template +class MonotonicRingMap { +public: + using key_type = KEY; //!< The key type. + using mapped_type = VALUE; //!< The value type. + using value_type = std::pair; //!< The key-value pair type. + + //! Creates an empty map. + MonotonicRingMap() noexcept = default; + + //! Returns the number of elements in the map. + size_t size() const noexcept { return mSize; } + + //! Returns the maximum number of elements the map can hold. + static constexpr size_t capacity() noexcept { return N; } + + //! Returns true if the map is empty. + bool empty() const noexcept { return mSize == 0; } + + //! Returns true if the map is full. + bool full() const noexcept { return mSize == N; } + + //! Clears the map entirely. + void clear() noexcept { mSize = 0; mHead = 0; } + + /** + * Inserts a new key-value pair. + * The key must be greater than the key of the last inserted element. + * If the map is full, the oldest element is overwritten. + * @param key The key to insert. + * @param value The value to associate with the key. + */ + UTILS_NOINLINE void insert(key_type key, mapped_type value) { + assert(empty() || key > back().first); // assert monotonic + if (UTILS_LIKELY(full())) { + // container is full, replace the oldest element + mStorage[mHead] = { key, value }; + mHead = (mHead + 1) % N; + } else { + // container is not full, add to the end + const uint32_t index = (mHead + mSize) % N; + mStorage[index] = { key, value }; + mSize++; + } + } + + /** + * Finds a value by its key. + * @param key The key to look for. + * @return A pointer to the value if the key is found, nullptr otherwise. + */ + mapped_type* find(key_type key) { + return const_cast(static_cast(this)->find(key)); + } + + /** + * Finds a value by its key. + * @param key The key to look for. + * @return A pointer to the const value if the key is found, nullptr otherwise. + */ + UTILS_NOINLINE const mapped_type* find(key_type key) const { + if (empty()) { + return nullptr; + } + + if (key < front().first || key > back().first) { + return nullptr; + } + + const auto comparator = [](const value_type& element, key_type k) { + return element.first < k; + }; + + const auto endOfStorage = mStorage.cbegin() + N; + const auto headIter = mStorage.cbegin() + mHead; + + if (mHead + mSize <= N) { + // The logical sequence is contiguous in memory + const auto logicalEnd = headIter + mSize; + auto it = std::lower_bound(headIter, logicalEnd, key, comparator); + if (it != logicalEnd && it->first == key) { + return &it->second; + } + } else { // Wrapped around + // First part: mStorage[mHead...N-1] + auto it1 = std::lower_bound(headIter, endOfStorage, key, comparator); + if (it1 != endOfStorage && it1->first == key) { + return &it1->second; + } + + // Second part: mStorage[0...head-1] + const auto wrapPartEndIter = mStorage.cbegin() + ((mHead + mSize) % N); + auto it2 = std::lower_bound(mStorage.cbegin(), wrapPartEndIter, key, comparator); + if (it2 != wrapPartEndIter && it2->first == key) { + return &it2->second; + } + } + return nullptr; + } + + //! Returns a reference to the oldest element. + const value_type& front() const { + assert(!empty()); + return mStorage[mHead]; + } + + //! Returns a reference to the newest element. + const value_type& back() const { + assert(!empty()); + return mStorage[(mHead + mSize - 1) % N]; + } + +private: + std::array mStorage; + uint32_t mSize = 0; + uint32_t mHead = 0; +}; + +} // namespace utils + +#endif // TNT_UTILS_MONOTONICRINGMAP_H diff --git a/package/ios/libs/filament/include/utils/NameComponentManager.h b/package/ios/libs/filament/include/utils/NameComponentManager.h index 4ac7435a..403cc011 100644 --- a/package/ios/libs/filament/include/utils/NameComponentManager.h +++ b/package/ios/libs/filament/include/utils/NameComponentManager.h @@ -47,7 +47,7 @@ class EntityManager; * printf("%s\n", names->getName(names->getInstance(myEntity)); * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager { +class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager { public: using Instance = EntityInstance; @@ -97,7 +97,7 @@ class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager const char* getName(Instance instance) const noexcept; void gc(EntityManager& em) noexcept { - SingleInstanceComponentManager::gc(em, [this](Entity e) { + SingleInstanceComponentManager::gc(em, [this](Entity e) { removeComponent(e); }); } diff --git a/package/ios/libs/filament/include/utils/Panic.h b/package/ios/libs/filament/include/utils/Panic.h index c658da4b..acd19468 100644 --- a/package/ios/libs/filament/include/utils/Panic.h +++ b/package/ios/libs/filament/include/utils/Panic.h @@ -17,14 +17,28 @@ #ifndef TNT_UTILS_PANIC_H #define TNT_UTILS_PANIC_H +#ifdef FILAMENT_PANIC_USES_ABSL +# if FILAMENT_PANIC_USES_ABSL +# include "absl/log/log.h" +# define FILAMENT_CHECK_PRECONDITION CHECK +# define FILAMENT_CHECK_POSTCONDITION CHECK +# define FILAMENT_CHECK_ARITHMETIC CHECK +# endif +#endif + #include #include +#include #include +#include #ifdef __EXCEPTIONS # define UTILS_EXCEPTIONS 1 #else +# ifdef UTILS_EXCEPTIONS +# error UTILS_EXCEPTIONS is already defined! +# endif #endif /** @@ -280,12 +294,24 @@ class UTILS_PUBLIC Panic { */ virtual const char* what() const noexcept = 0; + /** + * Get the type of the panic (e.g. "Precondition") + * @return a C string containing the type of panic + */ + virtual const char* getType() const noexcept = 0; + /** * Get the reason string for the panic * @return a C string containing the reason for the panic */ virtual const char* getReason() const noexcept = 0; + /** + * Get a version of the reason string that is guaranteed to be constructed from literal + * strings only; it will contain no runtime information. + */ + virtual const char* getReasonLiteral() const noexcept = 0; + /** * Get the function name where the panic was detected. On debug build the fully qualified * function name is returned; on release builds only the function name is. @@ -327,14 +353,16 @@ class UTILS_PUBLIC Panic { * The TPanic<> class implements the std::exception protocol as well as the Panic * interface common to all exceptions thrown by the framework. */ -template +template class UTILS_PUBLIC TPanic : public Panic { public: // std::exception protocol const char* what() const noexcept override; // Panic interface + const char* getType() const noexcept override; const char* getReason() const noexcept override; + const char* getReasonLiteral() const noexcept override; const char* getFunction() const noexcept override; const char* getFile() const noexcept override; int getLine() const noexcept override; @@ -348,13 +376,15 @@ class UTILS_PUBLIC TPanic : public Panic { * @param function the name of the function where the error was detected * @param file the file where the above function in implemented * @param line the line in the above file where the error was detected - * @param format printf style string describing the error + * @param literal a literal version of the error message + * @param format printf style format string describing the error + * @param ... printf style arguments * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC * @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC * @see setMode() */ - static void panic(char const* function, char const* file, int line, const char* format, ...) - UTILS_NORETURN; + static void panic(char const* function, char const* file, int line, char const* literal, + const char* format, ...) UTILS_NORETURN; /** * Depending on the mode set, either throws an exception of type T with the given reason plus @@ -363,43 +393,43 @@ class UTILS_PUBLIC TPanic : public Panic { * @param function the name of the function where the error was detected * @param file the file where the above function in implemented * @param line the line in the above file where the error was detected - * @param s std::string describing the error + * @param literal a literal version of the error message + * @param reason std::string describing the error * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC * @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC * @see setMode() */ - static inline void panic(char const* function, char const* file, int line, const std::string& s) - UTILS_NORETURN { - panic(function, file, line, s.c_str()); - } - -protected: - /** - * Creates a Panic. - * @param reason a description of the cause of the error - */ - explicit TPanic(std::string reason); + static void panic( + char const* function, char const* file, int line, char const* literal, + std::string reason) UTILS_NORETURN; +private: /** * Creates a Panic with extra information about the error-site. * @param function the name of the function where the error was detected * @param file the file where the above function in implemented * @param line the line in the above file where the error was detected + * @param literal a literal version of the error message * @param reason a description of the cause of the error */ - TPanic(char const* function, char const* file, int line, std::string reason); + TPanic(char const* function, char const* file, int line, char const* literal, + std::string reason); + friend class PreconditionPanic; + friend class PostconditionPanic; + friend class ArithmeticPanic; + +protected: ~TPanic() override; private: - void buildMessage(); - - CallStack m_callstack; - std::string m_reason; - char const* const m_function = nullptr; - char const* const m_file = nullptr; - const int m_line = -1; - mutable std::string m_msg; + char const* const mFile = nullptr; // file where the panic happened + char const* const mFunction = nullptr; // function where the panic happened + int const mLine = -1; // line where the panic happened + std::string mLiteral; // reason for the panic, built only from literals + std::string mReason; // reason for the panic + mutable std::string mWhat; // fully formatted reason + CallStack mCallstack; }; namespace details { @@ -416,11 +446,12 @@ void panicLog( * ASSERT_PRECONDITION uses this Panic to report a precondition failure. * @see ASSERT_PRECONDITION */ -class UTILS_PUBLIC PreconditionPanic : public TPanic { +class UTILS_PUBLIC PreconditionPanic final : public TPanic { // Programming error, can be avoided // e.g.: invalid arguments - using TPanic::TPanic; - friend class TPanic; + using TPanic::TPanic; + friend class TPanic; + constexpr static auto type = "Precondition"; }; /** @@ -429,11 +460,12 @@ class UTILS_PUBLIC PreconditionPanic : public TPanic { * ASSERT_POSTCONDITION uses this Panic to report a postcondition failure. * @see ASSERT_POSTCONDITION */ -class UTILS_PUBLIC PostconditionPanic : public TPanic { +class UTILS_PUBLIC PostconditionPanic final : public TPanic { // Usually only detectable at runtime - // e.g.: dead-lock would occur, arithmetic errors - using TPanic::TPanic; - friend class TPanic; + // e.g.: deadlock would occur, arithmetic errors + using TPanic::TPanic; + friend class TPanic; + constexpr static auto type = "Postcondition"; }; /** @@ -442,13 +474,100 @@ class UTILS_PUBLIC PostconditionPanic : public TPanic { * ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure. * @see ASSERT_ARITHMETIC */ -class UTILS_PUBLIC ArithmeticPanic : public TPanic { +class UTILS_PUBLIC ArithmeticPanic final : public TPanic { // A common case of post-condition error // e.g.: underflow, overflow, internal computations errors - using TPanic::TPanic; - friend class TPanic; + using TPanic::TPanic; + friend class TPanic; + constexpr static auto type = "Arithmetic"; +}; + +namespace details { + +struct Voidify final { + template + void operator&&(const T&) const&& {} +}; + +class UTILS_PUBLIC PanicStream { +public: + PanicStream( + char const* function, + char const* file, + int line, + char const* message) noexcept; + + ~PanicStream(); + + PanicStream& operator<<(short value) noexcept; + PanicStream& operator<<(unsigned short value) noexcept; + + PanicStream& operator<<(char value) noexcept; + PanicStream& operator<<(unsigned char value) noexcept; + + PanicStream& operator<<(int value) noexcept; + PanicStream& operator<<(unsigned int value) noexcept; + + PanicStream& operator<<(long value) noexcept; + PanicStream& operator<<(unsigned long value) noexcept; + + PanicStream& operator<<(long long value) noexcept; + PanicStream& operator<<(unsigned long long value) noexcept; + + PanicStream& operator<<(float value) noexcept; + PanicStream& operator<<(double value) noexcept; + PanicStream& operator<<(long double value) noexcept; + + PanicStream& operator<<(bool value) noexcept; + + PanicStream& operator<<(const void* value) noexcept; + + PanicStream& operator<<(const char* value) noexcept; + PanicStream& operator<<(const unsigned char* value) noexcept; + + PanicStream& operator<<(std::string const& value) noexcept; + PanicStream& operator<<(std::string_view const& value) noexcept; + +protected: + io::sstream mStream; + char const* mFunction; + char const* mFile; + int mLine; + char const* mLiteral; +}; + +template +class TPanicStream : public PanicStream { +public: + using PanicStream::PanicStream; + ~TPanicStream() noexcept(false) UTILS_NORETURN { + T::panic(mFunction, mFile, mLine, mLiteral, mStream.c_str()); + } +}; + +template +class FlagGuardedStream : public PanicStream { +public: + FlagGuardedStream(bool const enable, char const* function, char const* file, int const line, + char const* condition) + : PanicStream(function, file, line, condition), + mEnablePanic(enable) {} + ~FlagGuardedStream() noexcept(false) { + if (mEnablePanic) { + PanicType::panic(mFunction, mFile, mLine, mLiteral, mStream.c_str()); + } else { + logWarning(); + } + } + +private: + void logWarning() noexcept; + + bool mEnablePanic; }; +} // namespace details + // ----------------------------------------------------------------------------------------------- } // namespace utils @@ -460,37 +579,84 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { # define PANIC_FUNCTION __func__ #endif + +#define FILAMENT_CHECK_CONDITION_IMPL(cond) \ + switch (0) \ + case 0: \ + default: \ + UTILS_VERY_LIKELY(cond) ? (void)0 : ::utils::details::Voidify()&& + +#define FILAMENT_PANIC_IMPL(message, TYPE) \ + ::utils::details::TPanicStream<::utils::TYPE>(PANIC_FUNCTION, PANIC_FILE(__FILE__), __LINE__, message) + +#ifndef FILAMENT_CHECK_PRECONDITION +#define FILAMENT_CHECK_PRECONDITION(condition) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, PreconditionPanic) +#endif + +#ifndef FILAMENT_CHECK_POSTCONDITION +#define FILAMENT_CHECK_POSTCONDITION(condition) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, PostconditionPanic) +#endif + +#ifndef FILAMENT_CHECK_ARITHMETIC +#define FILAMENT_CHECK_ARITHMETIC(condition) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_PANIC_IMPL(#condition, ArithmeticPanic) +#endif + +#define FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, TYPE) \ + ::utils::details::FlagGuardedStream<::utils::TYPE>(flag, PANIC_FUNCTION, PANIC_FILE(__FILE__), \ + __LINE__, #condition) + +#ifndef FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION +#define FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(condition, flag) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PreconditionPanic) +#endif + +#ifndef FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION +#define FILAMENT_FLAG_GUARDED_CHECK_POSTCONDITION(condition, flag) \ + FILAMENT_CHECK_CONDITION_IMPL(condition) FILAMENT_FLAG_GUARDED_PANIC_IMPL(flag, condition, PostconditionPanic) +#endif + +#define PANIC_PRECONDITION_IMPL(cond, format, ...) \ + ::utils::PreconditionPanic::panic(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__) + +#define PANIC_POSTCONDITION_IMPL(cond, format, ...) \ + ::utils::PostconditionPanic::panic(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__) + +#define PANIC_ARITHMETIC_IMPL(cond, format, ...) \ + ::utils::ArithmeticPanic::panic(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, #cond, format, ##__VA_ARGS__) + +#define PANIC_LOG_IMPL(cond, format, ...) \ + ::utils::details::panicLog(PANIC_FUNCTION, \ + PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) + /** * PANIC_PRECONDITION is a macro that reports a PreconditionPanic * @param format printf-style string describing the error in more details */ -#define PANIC_PRECONDITION(format, ...) \ - ::utils::PreconditionPanic::panic(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_PRECONDITION(format, ...) PANIC_PRECONDITION_IMPL(format, format, ##__VA_ARGS__) /** * PANIC_POSTCONDITION is a macro that reports a PostconditionPanic * @param format printf-style string describing the error in more details */ -#define PANIC_POSTCONDITION(format, ...) \ - ::utils::PostconditionPanic::panic(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_POSTCONDITION(format, ...) PANIC_POSTCONDITION_IMPL(format, format, ##__VA_ARGS__) /** * PANIC_ARITHMETIC is a macro that reports a ArithmeticPanic * @param format printf-style string describing the error in more details */ -#define PANIC_ARITHMETIC(format, ...) \ - ::utils::ArithmeticPanic::panic(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_ARITHMETIC(format, ...) PANIC_ARITHMETIC_IMPL(format, format, ##__VA_ARGS__) /** * PANIC_LOG is a macro that logs a Panic, and continues as usual. * @param format printf-style string describing the error in more details */ -#define PANIC_LOG(format, ...) \ - ::utils::details::panicLog(PANIC_FUNCTION, \ - PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) +#define PANIC_LOG(format, ...) PANIC_LOG_IMPL(format, format, ##__VA_ARGS__) /** * @ingroup errors @@ -501,14 +667,14 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @param format printf-style string describing the error in more details */ #define ASSERT_PRECONDITION(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__) : (void)0) + (!UTILS_VERY_LIKELY(cond) ? PANIC_PRECONDITION_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) #define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_PRECONDITION_IMPL(cond, format, ##__VA_ARGS__), false : true) #else #define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__), false : true) #endif @@ -529,14 +695,14 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @endcode */ #define ASSERT_POSTCONDITION(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_POSTCONDITION(format, ##__VA_ARGS__) : (void)0) + (!UTILS_VERY_LIKELY(cond) ? PANIC_POSTCONDITION_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) #define ASSERT_POSTCONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_POSTCONDITION(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_POSTCONDITION_IMPL(cond, format, ##__VA_ARGS__), false : true) #else #define ASSERT_POSTCONDITION_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__), false : true) #endif /** @@ -557,14 +723,14 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * @endcode */ #define ASSERT_ARITHMETIC(cond, format, ...) \ - (!(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__) : (void)0) + (!(cond) ? PANIC_ARITHMETIC_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) #define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_ARITHMETIC_IMPL(cond, format, ##__VA_ARGS__), false : true) #else #define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \ - (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__), false : true) #endif /** @@ -588,6 +754,7 @@ class UTILS_PUBLIC ArithmeticPanic : public TPanic { * } * @endcode */ -#define ASSERT_DESTRUCTOR(cond, format, ...) (!(cond) ? PANIC_LOG(format, ##__VA_ARGS__) : (void)0) +#define ASSERT_DESTRUCTOR(cond, format, ...) \ + (!UTILS_VERY_LIKELY(cond) ? PANIC_LOG_IMPL(cond, format, ##__VA_ARGS__) : (void)0) #endif // TNT_UTILS_PANIC_H diff --git a/package/ios/libs/filament/include/utils/RefCountedMap.h b/package/ios/libs/filament/include/utils/RefCountedMap.h new file mode 100644 index 00000000..5fc0b85f --- /dev/null +++ b/package/ios/libs/filament/include/utils/RefCountedMap.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2025 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_REFCOUNTEDMAP_H +#define TNT_UTILS_REFCOUNTEDMAP_H + +#include +#include +#include + +#include + +#include +#include +#include + +namespace utils { + +namespace refcountedmap { + +template +struct is_pointer_like_trait : std::false_type {}; + +template +struct is_pointer_like_trait())>> : std::true_type {}; + +template +inline constexpr bool IsPointer = is_pointer_like_trait::value; + +template +struct PointerTraits { + using element_type = T; +}; + +template +struct PointerTraits>> { + using element_type = typename std::pointer_traits::element_type; +}; + +template +struct DefaultValue { + T operator()() const noexcept { + return {}; + } +}; + +} // namespace refcountedmap + +/** A reference-counted map. + * + * Don't use RAII here, both because we sometimes want to deliberately leak memory, and because + * we're managing GL resources that require more managed destruction. + */ +template, + typename NullValue = refcountedmap::DefaultValue> +class RefCountedMap { + // Use references for the key if the size of the key type is greater than the size of a pointer. + using KeyRef = std::conditional_t<(sizeof(Key) > sizeof(void*)), const Key&, Key>; + using TValue = typename refcountedmap::PointerTraits::element_type; + + struct Entry { + uint32_t referenceCount; + T value; + }; + + using Map = tsl::robin_map; + + static constexpr TValue& deref(T& a) { + if constexpr (refcountedmap::IsPointer) { + return *a; + } else { + return a; + } + } + + static constexpr TValue const& deref(T const& a) { + if constexpr (refcountedmap::IsPointer) { + return *a; + } else { + return a; + } + } + + static constexpr const char* UTILS_NONNULL MISSING_ENTRY_ERROR_STRING = + "Cache is missing entry"; + static constexpr const char* UTILS_NONNULL MISSING_VALUE_ERROR_STRING = + "Attempted to get missing value"; + +public: + /** Acquire and return a value by key, initializing it with F if it doesn't exist. + * + * If F returns NullValue{}(), this indicates a failure to create the object. If T is a value + * type, the returned pointer is valid only as long as the next call to acquire() or release(). + */ + template + TValue* UTILS_NULLABLE acquire(KeyRef key, size_t hash, F factory) noexcept { + auto it = mMap.find(key, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return &deref(it.value().value); + } + T r = factory(); + if (r == NullValue{}()) { + return nullptr; + } + // TODO: how to use above computed hash here? + return &deref(mMap.insert({ key, Entry{ 1, std::move(r) } }).first.value().value); + } + + template + inline TValue* UTILS_NULLABLE acquire(KeyRef key, F factory) noexcept { + return acquire(key, Hash{}(key), std::move(factory)); + } + + /** Acquire and return a pointer to the value if one exists. + * + * It's possible to acquire a key before its value is initialized, in which case this function + * returns nullptr. + * + * If T is a value type, this pointer is valid only as long as the next call to acquire() or + * release(). + */ + TValue* UTILS_NULLABLE acquire(KeyRef key, size_t hash) noexcept { + auto it = mMap.find(key, hash); + if (it != mMap.end()) { + it.value().referenceCount++; + return &deref(it.value().value); + } + // TODO: how to use above computed hash here? + mMap.insert({ key, Entry{ 1, NullValue{}() } }); + return nullptr; + } + + inline TValue* UTILS_NULLABLE acquire(KeyRef key) noexcept { + return acquire(key, Hash{}(key)); + } + + /** Release a reference to key, destroying it with F if reference count reaches zero. + * + * Panics if no entry found in map. + */ + template + void release(KeyRef key, size_t hash, F releaser) { + auto it = mMap.find(key, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + if (--it.value().referenceCount == 0) { + if (it.value().value != NullValue{}()){ + releaser(deref(it.value().value)); + } + // TODO: change to erase_fast + mMap.erase(it); + } + } + + template + inline void release(KeyRef key, F releaser) noexcept { + release(key, Hash{}(key), std::move(releaser)); + } + + /** Release a reference to key. + * + * Panics if no entry found in map. + */ + void release(KeyRef key, size_t hash) { + auto it = mMap.find(key, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + if (--it.value().referenceCount == 0) { + // TODO: change to erase_fast + mMap.erase(it); + } + } + + inline void release(KeyRef key) noexcept { + release(key, Hash{}(key)); + } + + /** Get a value by key, initializing it with F if it doesn't exist. + * + * If F returns NullValue{}(), this indicates a failure to create the object. If T is a value + * type, the returned pointer is valid only as long as the next call to acquire() or release(). + */ + template + TValue* UTILS_NULLABLE get(KeyRef key, size_t hash, F factory) { + auto it = mMap.find(key, hash); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + const T nullValue = NullValue{}(); + if (it.value().value == nullValue) { + it.value().value = factory(); + if (it.value().value == nullValue) { + return nullptr; + } + } + return &deref(it.value().value); + } + + template + inline TValue* UTILS_NULLABLE get(KeyRef key, F factory) noexcept { + return get(key, Hash{}(key), std::move(factory)); + } + + /** Return reference to existing value by key. + * + * This reference is valid only as long as the next call to acquire() or release(). + * + * Panics if no entry found in map. + */ + TValue& get(KeyRef key, size_t hash) { + auto it = mMap.find(key); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + FILAMENT_CHECK_PRECONDITION(it.value().value != NullValue{}()) + << MISSING_VALUE_ERROR_STRING; + return deref(it.value().value); + } + + inline TValue& get(KeyRef key) noexcept { return get(key, Hash{}(key)); } + + TValue const& get(KeyRef key, size_t hash) const { + auto it = mMap.find(key); + FILAMENT_CHECK_PRECONDITION(it != mMap.end()) << MISSING_ENTRY_ERROR_STRING; + FILAMENT_CHECK_PRECONDITION(it.value().value != NullValue{}()) + << MISSING_VALUE_ERROR_STRING; + return deref(it->second.value); + } + + inline TValue const& get(KeyRef key) const noexcept { return get(key, Hash{}(key)); } + + /** Returns true if the map is empty. */ + inline bool empty() const noexcept { return mMap.empty(); } + +private: + Map mMap; +}; + +} + +#endif // TNT_UTILS_REFCOUNTEDMAP_H diff --git a/package/ios/libs/filament/include/utils/SingleInstanceComponentManager.h b/package/ios/libs/filament/include/utils/SingleInstanceComponentManager.h index ddd538f5..fc820ca2 100644 --- a/package/ios/libs/filament/include/utils/SingleInstanceComponentManager.h +++ b/package/ios/libs/filament/include/utils/SingleInstanceComponentManager.h @@ -108,7 +108,7 @@ class UTILS_PUBLIC SingleInstanceComponentManager { return getComponentCount() == 0; } - utils::Entity const* getEntities() const noexcept { + Entity const* getEntities() const noexcept { return data() + 1; } @@ -238,11 +238,11 @@ class UTILS_PUBLIC SingleInstanceComponentManager { default_random_engine& rng = mRng; UTILS_NOUNROLL while (count && aliveInARow < ratio) { - assert_invariant(count == getComponentCount()); + assert(count == getComponentCount()); // note: using the modulo favorizes lower number size_t const i = rng() % count; Entity const entity = pEntities[i]; - assert_invariant(entity); + assert(entity); if (UTILS_LIKELY(em.isAlive(entity))) { ++aliveInARow; continue; diff --git a/package/ios/libs/filament/include/utils/Slice.h b/package/ios/libs/filament/include/utils/Slice.h index 444a3b27..59f1043d 100644 --- a/package/ios/libs/filament/include/utils/Slice.h +++ b/package/ios/libs/filament/include/utils/Slice.h @@ -17,8 +17,11 @@ #ifndef TNT_UTILS_SLICE_H #define TNT_UTILS_SLICE_H +#include #include +#include +#include #include #include @@ -26,116 +29,126 @@ namespace utils { -/* - * A fixed-size slice of a container +/** A fixed-size slice of a container. + * + * Analogous to std::span. */ -template +template class Slice { public: + using element_type = T; + using value_type = std::remove_cv_t; + using size_type = size_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; using iterator = T*; using const_iterator = T const*; - using value_type = T; - using reference = T&; - using const_reference = T const&; - using pointer = T*; - using const_pointer = T const*; - using size_type = size_t; - Slice() noexcept = default; + Slice() = default; + Slice(iterator begin, iterator end) noexcept : mBegin(begin), mEnd(end) {} + Slice(pointer begin, size_type count) noexcept : mBegin(begin), mEnd(begin + count) {} - Slice(const_iterator begin, const_iterator end) noexcept - : mBegin(const_cast(begin)), - mEnd(const_cast(end)) { - } + Slice(Slice const& rhs) : mBegin(rhs.begin()), mEnd(rhs.end()) {} - Slice(const_pointer begin, size_type count) noexcept - : mBegin(const_cast(begin)), - mEnd(mBegin + count) { + // If Slice is Slice, define coercive copy constructor from Slice. + template + Slice(std::enable_if_t, Slice const&> rhs) + : mBegin(rhs.begin()), mEnd(rhs.end()) {} + + Slice& operator=(Slice const& rhs) noexcept { + mBegin = rhs.begin(); + mEnd = rhs.end(); + return *this; } - Slice(Slice const& rhs) noexcept = default; - Slice(Slice&& rhs) noexcept = default; - Slice& operator=(Slice const& rhs) noexcept = default; - Slice& operator=(Slice&& rhs) noexcept = default; + // If Slice is Slice, define assignment operator from Slice. + template + Slice& operator=( + std::enable_if_t, Slice const&> rhs) noexcept { + mBegin = rhs.begin(); + mEnd = rhs.end(); + return *this; + } - void set(pointer begin, size_type count) UTILS_RESTRICT noexcept { + void set(pointer begin, size_type count) noexcept { mBegin = begin; mEnd = begin + count; } - void set(iterator begin, iterator end) UTILS_RESTRICT noexcept { + void set(iterator begin, iterator end) noexcept { mBegin = begin; mEnd = end; } - void swap(Slice& rhs) UTILS_RESTRICT noexcept { + void swap(Slice& rhs) noexcept { std::swap(mBegin, rhs.mBegin); std::swap(mEnd, rhs.mEnd); } - void clear() UTILS_RESTRICT noexcept { + void clear() noexcept { mBegin = nullptr; mEnd = nullptr; } + bool operator==(Slice const& rhs) const noexcept { + if (size() != rhs.size()) { + return false; + } + if (mBegin == rhs.cbegin()) { + return true; + } + return std::equal(cbegin(), cend(), rhs.cbegin()); + } + + bool operator==(Slice const& rhs) const noexcept { + return *this == Slice(rhs); + } + // size - size_t size() const UTILS_RESTRICT noexcept { return mEnd - mBegin; } - size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); } - bool empty() const UTILS_RESTRICT noexcept { return size() == 0; } + size_t size() const noexcept { return mEnd - mBegin; } + size_t sizeInBytes() const noexcept { return size() * sizeof(T); } + bool empty() const noexcept { return size() == 0; } // iterators - iterator begin() UTILS_RESTRICT noexcept { return mBegin; } - const_iterator begin() const UTILS_RESTRICT noexcept { return mBegin; } - const_iterator cbegin() const UTILS_RESTRICT noexcept { return this->begin(); } - iterator end() UTILS_RESTRICT noexcept { return mEnd; } - const_iterator end() const UTILS_RESTRICT noexcept { return mEnd; } - const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); } + iterator begin() const noexcept { return mBegin; } + const_iterator cbegin() const noexcept { return this->begin(); } + iterator end() const noexcept { return mEnd; } + const_iterator cend() const noexcept { return this->end(); } // data access - reference operator[](size_t n) UTILS_RESTRICT noexcept { + reference operator[](size_t n) const noexcept { assert(n < size()); return mBegin[n]; } - const_reference operator[](size_t n) const UTILS_RESTRICT noexcept { - assert(n < size()); - return mBegin[n]; - } - - reference at(size_t n) UTILS_RESTRICT noexcept { + reference at(size_t n) const noexcept { return operator[](n); } - const_reference at(size_t n) const UTILS_RESTRICT noexcept { - return operator[](n); - } - - reference front() UTILS_RESTRICT noexcept { + reference front() const noexcept { assert(!empty()); return *mBegin; } - const_reference front() const UTILS_RESTRICT noexcept { - assert(!empty()); - return *mBegin; - } - - reference back() UTILS_RESTRICT noexcept { + reference back() const noexcept { assert(!empty()); return *(this->end() - 1); } - const_reference back() const UTILS_RESTRICT noexcept { - assert(!empty()); - return *(this->end() - 1); - } - - pointer data() UTILS_RESTRICT noexcept { + pointer data() const noexcept { return this->begin(); } - const_pointer data() const UTILS_RESTRICT noexcept { - return this->begin(); + template> + size_t hash() const noexcept { + Hash hasher; + size_t seed = size(); + for (auto const& it : *this) { + utils::hash::combine_fast(seed, hasher(it)); + } + return seed; } protected: @@ -145,4 +158,13 @@ class Slice { } // namespace utils +namespace std { + +template +struct hash> { + inline size_t operator()(utils::Slice const& lhs) const noexcept { return lhs.hash(); } +}; + +} // namespace std + #endif // TNT_UTILS_SLICE_H diff --git a/package/ios/libs/filament/include/utils/StaticString.h b/package/ios/libs/filament/include/utils/StaticString.h new file mode 100644 index 00000000..ae2008cb --- /dev/null +++ b/package/ios/libs/filament/include/utils/StaticString.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2025 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_STATICSTRING_H +#define TNT_UTILS_STATICSTRING_H + +#include + +#include + +#include + +namespace utils { + +class ImmutableCString; + +/** + * @brief A lightweight string class that stores a pointer to a string literal and its size, without dynamic allocation. + * + * This class is designed to efficiently represent string literals. It does not allocate any memory + * and instead relies on the compiler to manage the memory of the string literals. + */ +class UTILS_PUBLIC StaticString { +public: + using value_type = std::string_view::value_type; + using const_pointer = std::string_view::const_pointer; + using const_reference = std::string_view::const_reference; + using size_type = std::string_view::size_type; + using const_iterator = std::string_view::const_iterator; + + // Constructor from string literal + template + constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor) + + constexpr StaticString() noexcept : mString("", 0) {} + + constexpr const_pointer c_str() const noexcept { return mString.data(); } + constexpr const_pointer data() const noexcept { return mString.data(); } + constexpr size_type size() const noexcept { return mString.size(); } + constexpr size_type length() const noexcept { return mString.size(); } + constexpr bool empty() const noexcept { return mString.empty(); } + + constexpr const_iterator begin() const noexcept { return mString.begin(); } + constexpr const_iterator end() const noexcept { return mString.end(); } + constexpr const_iterator cbegin() const noexcept { return mString.begin(); } + constexpr const_iterator cend() const noexcept { return mString.end(); } + + constexpr const_reference operator[](size_type const pos) const noexcept { + return mString[pos]; + } + + constexpr const_reference at(size_type const pos) const { + return mString[pos]; + } + + constexpr const_reference front() const noexcept { + return mString.front(); + } + + constexpr const_reference back() const noexcept { + return mString.back(); + } + + constexpr int compare(const StaticString& rhs) const noexcept { + return mString.compare(rhs.mString); + } + +private: +#if !defined(NDEBUG) + friend io::ostream& operator<<(io::ostream& out, const ImmutableCString& rhs); +#endif + + std::string_view mString; + + friend constexpr bool operator==(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString == rhs.mString; + } + + friend constexpr bool operator!=(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString != rhs.mString; + } + + friend constexpr bool operator<(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString < rhs.mString; + } + + friend constexpr bool operator>(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString > rhs.mString; + } + + friend constexpr bool operator<=(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString <= rhs.mString; + } + + friend constexpr bool operator>=(const StaticString& lhs, const StaticString& rhs) noexcept { + return lhs.mString >= rhs.mString; + } +}; + +} // namespace utils + +#endif // TNT_UTILS_STATICSTRING_H diff --git a/package/ios/libs/filament/include/utils/Status.h b/package/ios/libs/filament/include/utils/Status.h new file mode 100644 index 00000000..22a8eb4f --- /dev/null +++ b/package/ios/libs/filament/include/utils/Status.h @@ -0,0 +1,162 @@ +/* +* Copyright (C) 2025 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_STATUS_H +#define TNT_UTILS_STATUS_H + +#include +#include +#include +#include + +namespace utils { + +/** + * A code indicating the success or failure of an operation. + */ +enum class StatusCode { + /** The operation completed successfully. */ + OK, + /** The caller provided invalid arguments in the request. */ + INVALID_ARGUMENT, + /** Internal error was occurred while processing the request. */ + INTERNAL, + /** The requested operation is not supported. */ + UNSUPPORTED, +}; + +/** + * Returns the StatusCode to indicate whether the request was successful. + * If successful, it returns OK with an optional message, if not it returns + * other codes with an optional error message. + */ +class UTILS_PUBLIC Status { +public: + /** + * Creates a new Status with a StatusCode of OK. + */ + Status() : mStatusCode(StatusCode::OK) {} + + /** + * Creates a new Status with the given status code and supplementary message. + * + * @param statusCode The status code to use. + * @param message An optional message, usually contains the reason for the failure. + */ + Status(StatusCode statusCode, std::string_view message) : + mStatusCode(statusCode), + mMessage(message.data(), message.length()) {} + + Status(const Status& other) = default; + + Status(Status&& other) noexcept = default; + + ~Status() = default; + + Status& operator=(const Status& other) = default; + Status& operator=(Status&& other) noexcept = default; + + bool operator==(const Status& other) const { + return mStatusCode == other.mStatusCode && mMessage == other.mMessage; + } + + bool operator!=(const Status& other) const { + return !(*this == other); + } + + /** + * Returns true if the status is OK. + * @return true if the operation was successful, false otherwise. + */ + bool isOk() const { + return mStatusCode == StatusCode::OK; + } + + /** + * Returns the StatusCode for this Status. + * @return the StatusCode for this Status. + */ + StatusCode getCode() const { + return mStatusCode; + } + + /** + * Returns the message for this Status. + * @return The message string. Can be empty if it's not set. + */ + std::string_view getMessage() const { + return std::string_view(mMessage.begin(), mMessage.length()); + } + + /** + * Convenient factory functions for creating Status objects. + * Example usage: `return utils::Status::internal("internal error");` + */ + + /** + * Creates a success Status with a StatusCode of OK. + * @return a success Status with a StatusCode of OK + */ + static Status ok() { + return {}; + } + + /** + * Creates a success Status with a StatusCode of OK with a supplementary message. + * @return a success Status with a StatusCode of OK with a supplementary message. + */ + static Status ok(std::string_view message) { + return {StatusCode::OK, message}; + } + + /** + * Creates an error Status with an INTERNAL status code. + * @param message The error message to include. + * @return an error Status with an INTERNAL status code. + */ + static Status internal(std::string_view message) { + return {StatusCode::INTERNAL, message}; + } + + /** + * Creates an error Status with an INVALID_ARGUMENT status code. + * @param message The error message to include. + * @return an error Status with an INVALID_ARGUMENT status code. + */ + static Status invalidArgument(std::string_view message) { + return {StatusCode::INVALID_ARGUMENT, message}; + } + + /** + * Creates an error Status with an UNSUPPORTED status code. + * @param message The error message to include. + * @return an error Status with an UNSUPPORTED status code. + */ + static Status unsupported(std::string_view message) { + return { StatusCode::UNSUPPORTED, message }; + } + +private: + StatusCode mStatusCode; + // Additional message for the Status. Usually contains the reason for the error. + utils::CString mMessage; +}; + +utils::io::ostream& operator<<(utils::io::ostream& os, const Status& status); + +} // namespace utils + +#endif // TNT_UTILS_STATUS_H diff --git a/package/ios/libs/filament/include/utils/StructureOfArrays.h b/package/ios/libs/filament/include/utils/StructureOfArrays.h index a4309584..923744be 100644 --- a/package/ios/libs/filament/include/utils/StructureOfArrays.h +++ b/package/ios/libs/filament/include/utils/StructureOfArrays.h @@ -20,14 +20,8 @@ #include #include #include -#include #include -#include -#include -#include -#include - #include #include // note: this is safe, see how std::array is used below (inline / private) #include @@ -35,6 +29,12 @@ #include #include +#include +#include +#include +#include +#include + namespace utils { template @@ -130,7 +130,7 @@ class StructureOfArraysBase { friend class IteratorValueRef; friend iterator; friend const_iterator; - using Type = std::tuple::type...>; + using Type = std::tuple...>; Type elements; template @@ -368,7 +368,7 @@ class StructureOfArraysBase { size_t last = mSize++; // Fold expression on the comma operator ([&]{ - new(std::get(mArrays) + last) Elements{std::get(args)}; + new(std::get(mArrays) + last) Elements{std::get(std::forward(args))}; }() , ...); } @@ -513,7 +513,7 @@ class StructureOfArraysBase { return (soa.elementAt(i) = other); } UTILS_ALWAYS_INLINE Type const& operator = (Type&& other) noexcept { - return (soa.elementAt(i) = other); + return (soa.elementAt(i) = std::forward(other)); } // comparisons UTILS_ALWAYS_INLINE bool operator==(Type const& other) const { @@ -535,29 +535,29 @@ class StructureOfArraysBase { private: template - inline typename std::enable_if::type + inline std::enable_if_t for_each(std::tuple&, FuncT) {} template - inline typename std::enable_if::type + inline std::enable_if_t for_each(std::tuple& t, FuncT f) { f(I, std::get(t)); for_each(t, f); } template - inline typename std::enable_if::type + inline std::enable_if_t for_each_index(std::tuple&, FuncT) {} template - inline typename std::enable_if::type + inline std::enable_if_t for_each_index(std::tuple& t, FuncT f) { f.template operator()(std::get(t)); for_each_index(t, f); } inline void resizeNoCheck(size_t needed) noexcept { - assert_invariant(mCapacity >= needed); + assert(mCapacity >= needed); if (needed < mSize) { // we shrink the arrays destroy_each(needed, mSize); @@ -590,14 +590,14 @@ class StructureOfArraysBase { size_t unalignment = (offsets[i - 1] + sizes[i - 1]) % alignments[i]; size_t alignment = unalignment ? (alignments[i] - unalignment) : 0; offsets[i] = offsets[i - 1] + (sizes[i - 1] + alignment); - assert_invariant(offsets[i] % alignments[i] == 0); + assert(offsets[i] % alignments[i] == 0); } return offsets; } void construct_each(size_t from, size_t to) noexcept { forEach([from, to](auto p) { - using T = typename std::decay::type; + using T = std::decay_t; // note: scalar types like int/float get initialized to zero if constexpr (!std::is_trivially_default_constructible_v) { for (size_t i = from; i < to; i++) { @@ -609,7 +609,7 @@ class StructureOfArraysBase { void destroy_each(size_t from, size_t to) noexcept { forEach([from, to](auto p) { - using T = typename std::decay::type; + using T = std::decay_t; if constexpr (!std::is_trivially_destructible_v) { for (size_t i = from; i < to; i++) { p[i].~T(); @@ -624,7 +624,7 @@ class StructureOfArraysBase { if (mSize) { auto size = mSize; // placate a compiler warning forEach([buffer, &index, &offsets, size](auto p) { - using T = typename std::decay::type; + using T = std::decay_t; T* UTILS_RESTRICT b = static_cast(buffer); // go through each element and move them from the old array to the new @@ -671,7 +671,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::operator=( - StructureOfArraysBase::IteratorValueRef const& rhs) { + IteratorValueRef const& rhs) { return operator=(IteratorValue(rhs)); } @@ -679,7 +679,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::operator=( - StructureOfArraysBase::IteratorValueRef&& rhs) noexcept { + IteratorValueRef&& rhs) noexcept { return operator=(IteratorValue(rhs)); } @@ -688,7 +688,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::assign( - StructureOfArraysBase::IteratorValue const& rhs, std::index_sequence) { + IteratorValue const& rhs, std::index_sequence) { // implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue const& rhs) auto UTILS_UNUSED l = { (soa->elementAt(index) = std::get(rhs.elements), 0)... }; return *this; @@ -699,7 +699,7 @@ template inline typename StructureOfArraysBase::IteratorValueRef& StructureOfArraysBase::IteratorValueRef::assign( - StructureOfArraysBase::IteratorValue&& rhs, std::index_sequence) noexcept { + IteratorValue&& rhs, std::index_sequence) noexcept { // implements IteratorValueRef& IteratorValueRef::operator=(IteratorValue&& rhs) noexcept auto UTILS_UNUSED l = { (soa->elementAt(index) = std::move(std::get(rhs.elements)), 0)... }; diff --git a/package/ios/libs/filament/include/utils/Systrace.h b/package/ios/libs/filament/include/utils/Systrace.h index 9f5a7f2a..cc99ad39 100644 --- a/package/ios/libs/filament/include/utils/Systrace.h +++ b/package/ios/libs/filament/include/utils/Systrace.h @@ -14,10 +14,16 @@ * limitations under the License. */ +/*********************************************************************** + * DEPRECATED * + * * + * Systrace.h is deprecated as a public API. There is no replacement. * + * Applications should instead use the Perfetto SDK directly. * + ***********************************************************************/ + #ifndef TNT_UTILS_SYSTRACE_H #define TNT_UTILS_SYSTRACE_H - #define SYSTRACE_TAG_NEVER (0) #define SYSTRACE_TAG_ALWAYS (1<<0) #define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles diff --git a/package/ios/libs/filament/include/utils/algorithm.h b/package/ios/libs/filament/include/utils/algorithm.h index ea5ca44f..c4f400f4 100644 --- a/package/ios/libs/filament/include/utils/algorithm.h +++ b/package/ios/libs/filament/include/utils/algorithm.h @@ -22,6 +22,7 @@ #include // for std::enable_if #include +#include #include namespace utils { @@ -38,14 +39,20 @@ constexpr inline T popcount(T v) noexcept { return (T) (v * (ONES / 255)) >> (sizeof(T) - 1) * CHAR_BIT; } -template::value>> +template>> constexpr inline T clz(T x) noexcept { static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits"); x |= (x >> 1u); x |= (x >> 2u); - x |= (x >> 4u); - x |= (x >> 8u); - x |= (x >> 16u); + if constexpr (sizeof(T) * CHAR_BIT >= 8) { // just to silence compiler warning + x |= (x >> 4u); + } + if constexpr (sizeof(T) * CHAR_BIT >= 16) { // just to silence compiler warning + x |= (x >> 8u); + } + if constexpr (sizeof(T) * CHAR_BIT >= 32) { // just to silence compiler warning + x |= (x >> 16u); + } if constexpr (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning x |= (x >> 32u); } @@ -55,7 +62,7 @@ constexpr inline T clz(T x) noexcept { return T(sizeof(T) * CHAR_BIT) - details::popcount(x); } -template::value>> +template>> constexpr inline T ctz(T x) noexcept { static_assert(sizeof(T) * CHAR_BIT <= 64, "details::ctz() only support up to 64 bits"); T c = sizeof(T) * CHAR_BIT; @@ -67,11 +74,15 @@ constexpr inline T ctz(T x) noexcept { x &= -x; #endif if (x) c--; - if (sizeof(T) * CHAR_BIT >= 64) { + if constexpr (sizeof(T) * CHAR_BIT >= 64) { if (x & T(0x00000000FFFFFFFF)) c -= 32; } - if (x & T(0x0000FFFF0000FFFF)) c -= 16; - if (x & T(0x00FF00FF00FF00FF)) c -= 8; + if constexpr (sizeof(T) * CHAR_BIT >= 32) { + if (x & T(0x0000FFFF0000FFFF)) c -= 16; + } + if constexpr (sizeof(T) * CHAR_BIT >= 16) { + if (x & T(0x00FF00FF00FF00FF)) c -= 8; + } if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4; if (x & T(0x3333333333333333)) c -= 2; if (x & T(0x5555555555555555)) c -= 1; @@ -80,6 +91,24 @@ constexpr inline T ctz(T x) noexcept { } // namespace details +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned char x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz((unsigned int)x) - 24; +#else + return details::clz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned short x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz((unsigned int)x) - 16; +#else + return details::clz(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept { #if __has_builtin(__builtin_clz) @@ -107,6 +136,24 @@ unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept { #endif } +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned char x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned short x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept { #if __has_builtin(__builtin_ctz) @@ -134,6 +181,24 @@ unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept { #endif } +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned char x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned short x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + constexpr inline UTILS_PUBLIC UTILS_PURE unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept { #if __has_builtin(__builtin_popcount) @@ -161,13 +226,8 @@ unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept { #endif } -constexpr inline UTILS_PUBLIC UTILS_PURE -uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept { - return (uint8_t)popcount((unsigned int)x); -} - template::value && std::is_unsigned::value>> + typename = std::enable_if_t && std::is_unsigned_v>> constexpr inline UTILS_PUBLIC UTILS_PURE T log2i(T x) noexcept { return (sizeof(x) * 8 - 1u) - clz(x); diff --git a/package/ios/libs/filament/include/utils/bitset.h b/package/ios/libs/filament/include/utils/bitset.h index 281e5dfc..314ad2b8 100644 --- a/package/ios/libs/filament/include/utils/bitset.h +++ b/package/ios/libs/filament/include/utils/bitset.h @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -27,6 +26,7 @@ #include // for std::fill #include +#include #include #if defined(__ARM_NEON) @@ -45,8 +45,8 @@ namespace utils { */ template::value && - std::is_unsigned::value>::type> + typename = std::enable_if_t && + std::is_unsigned_v>> class UTILS_PUBLIC bitset { T storage[N]; @@ -60,13 +60,18 @@ class UTILS_PUBLIC bitset { std::fill(std::begin(storage), std::end(storage), 0); } + template> + explicit bitset(U value) noexcept { + storage[0] = value; + } + T getBitsAt(size_t n) const noexcept { - assert_invariant(n::max(); + } + size_t size() const noexcept { return N * BITS_PER_WORD; } + bool empty() const noexcept { return none(); } + bool test(size_t bit) const noexcept { return operator[](bit); } void set(size_t b) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] |= T(1) << (b % BITS_PER_WORD); } void set(size_t b, bool value) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD)); storage[b / BITS_PER_WORD] |= T(value) << (b % BITS_PER_WORD); } void unset(size_t b) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD)); } void flip(size_t b) noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD); } - void reset() noexcept { std::fill(std::begin(storage), std::end(storage), 0); } + void clear() noexcept { + reset(); + } + bool operator[](size_t b) const noexcept { - assert_invariant(b / BITS_PER_WORD < N); + assert(b / BITS_PER_WORD < N); return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD))); } diff --git a/package/ios/libs/filament/include/utils/compiler.h b/package/ios/libs/filament/include/utils/compiler.h index 710c901e..79ee3dda 100644 --- a/package/ios/libs/filament/include/utils/compiler.h +++ b/package/ios/libs/filament/include/utils/compiler.h @@ -33,7 +33,7 @@ #if __has_attribute(visibility) # define UTILS_PUBLIC __attribute__((visibility("default"))) #else -# define UTILS_PUBLIC +# define UTILS_PUBLIC #endif #if __has_attribute(deprecated) @@ -104,6 +104,19 @@ # define UTILS_UNLIKELY( exp ) (!!(exp)) #endif +#if __has_builtin(__builtin_expect_with_probability) +# ifdef __cplusplus +# define UTILS_VERY_LIKELY( exp ) (__builtin_expect_with_probability( !!(exp), true, 0.995 )) +# define UTILS_VERY_UNLIKELY( exp ) (__builtin_expect_with_probability( !!(exp), false, 0.995 )) +# else +# define UTILS_VERY_LIKELY( exp ) (__builtin_expect_with_probability( !!(exp), 1, 0.995 )) +# define UTILS_VERY_UNLIKELY( exp ) (__builtin_expect_with_probability( !!(exp), 0, 0.995 )) +# endif +#else +# define UTILS_VERY_LIKELY( exp ) (!!(exp)) +# define UTILS_VERY_UNLIKELY( exp ) (!!(exp)) +#endif + #if __has_builtin(__builtin_prefetch) # define UTILS_PREFETCH( exp ) (__builtin_prefetch(exp)) #else @@ -251,7 +264,7 @@ typedef SSIZE_T ssize_t; #if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__) # define __PRETTY_FUNCTION__ __FUNCSIG__ -#endif +#endif #if defined(_MSC_VER) diff --git a/package/ios/libs/filament/include/utils/debug.h b/package/ios/libs/filament/include/utils/debug.h index 0f6ecdb2..7587c12f 100644 --- a/package/ios/libs/filament/include/utils/debug.h +++ b/package/ios/libs/filament/include/utils/debug.h @@ -28,7 +28,7 @@ void panic(const char *func, const char * file, int line, const char *assertion) # define assert_invariant(e) ((void)0) #else # define assert_invariant(e) \ - (UTILS_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e)) + (UTILS_VERY_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e)) #endif // NDEBUG #endif // TNT_UTILS_DEBUG_H diff --git a/package/ios/libs/filament/include/utils/memalign.h b/package/ios/libs/filament/include/utils/memalign.h index 4c048bff..a7043310 100644 --- a/package/ios/libs/filament/include/utils/memalign.h +++ b/package/ios/libs/filament/include/utils/memalign.h @@ -32,7 +32,7 @@ namespace utils { inline void* aligned_alloc(size_t size, size_t align) noexcept { // 'align' must be a power of two and a multiple of sizeof(void*) align = (align < sizeof(void*)) ? sizeof(void*) : align; - assert(align && !(align & align - 1)); + assert(align && !(align & (align - 1))); assert((align % sizeof(void*)) == 0); void* p = nullptr; @@ -40,7 +40,7 @@ inline void* aligned_alloc(size_t size, size_t align) noexcept { #if defined(WIN32) p = ::_aligned_malloc(size, align); #else - ::posix_memalign(&p, align, size); + (void) ::posix_memalign(&p, align, size); #endif return p; } @@ -73,8 +73,8 @@ class STLAlignedAllocator { using const_pointer = const TYPE*; using reference = TYPE&; using const_reference = const TYPE&; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; + using size_type = ::size_t; + using difference_type = ::ptrdiff_t; using propagate_on_container_move_assignment = std::true_type; using is_always_equal = std::true_type; diff --git a/package/ios/libs/filament/include/utils/ostream.h b/package/ios/libs/filament/include/utils/ostream.h index cde8e75b..ea4952cc 100644 --- a/package/ios/libs/filament/include/utils/ostream.h +++ b/package/ios/libs/filament/include/utils/ostream.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,7 @@ namespace utils::io { struct ostream_; -class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { +class UTILS_PUBLIC ostream : protected PrivateImplementation { friend struct ostream_; public: @@ -95,11 +96,11 @@ class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { size_t length() const noexcept; private: - void reserve(size_t newSize) noexcept; + void reserve(size_t newCapacity) noexcept; char* buffer = nullptr; // buffer address char* curr = nullptr; // current pointer - size_t size = 0; // size remaining + size_t sizeRemaining = 0; // size remaining size_t capacity = 0; // total capacity of the buffer }; @@ -122,11 +123,6 @@ class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { const char* getFormat(type t) const noexcept; }; -// handles utils::bitset -inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept { - return o << (void*)uintptr_t(s.getValue()); -} - // handles vectors from libmath (but we do this generically, without needing a dependency on libmath) template class VECTOR, typename T> inline ostream& operator<<(ostream& stream, const VECTOR& v) { @@ -144,4 +140,21 @@ inline ostream& endl(ostream& s) noexcept { return flush(s << '\n'); } } // namespace utils::io +namespace utils { + +// handles utils::bitset + +namespace io { +class ostream; +} + +inline std::ostream& operator<<(std::ostream& o, bitset32 const& s) noexcept { + return o << (void*) uintptr_t(s.getValue()); +} +inline io::ostream& operator<<(io::ostream& o, bitset32 const& s) noexcept { + return o << (void*) uintptr_t(s.getValue()); +} +}// namespace utils + + #endif // TNT_UTILS_OSTREAM_H diff --git a/package/ios/libs/filament/include/utils/sstream.h b/package/ios/libs/filament/include/utils/sstream.h new file mode 100644 index 00000000..58d14fef --- /dev/null +++ b/package/ios/libs/filament/include/utils/sstream.h @@ -0,0 +1,36 @@ +/* + * 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_SSTREAM_H +#define TNT_UTILS_SSTREAM_H + +#include +#include + +#include + +namespace utils::io { + +class UTILS_PUBLIC sstream : public ostream { +public: + ostream& flush() noexcept override; + const char* c_str() const noexcept; + size_t length() const noexcept; +}; + +} // namespace utils::io + +#endif // TNT_UTILS_SSTREAM_H diff --git a/package/ios/libs/filament/include/viewer/AutomationEngine.h b/package/ios/libs/filament/include/viewer/AutomationEngine.h index 8747f59d..3c7257f2 100644 --- a/package/ios/libs/filament/include/viewer/AutomationEngine.h +++ b/package/ios/libs/filament/include/viewer/AutomationEngine.h @@ -56,6 +56,22 @@ class UTILS_PUBLIC AutomationEngine { * Allows users to toggle screenshots, change the sleep duration between tests, etc. */ struct Options { + + /** + * Formats that could be used for exporting the screenshots. + */ + enum class ExportFormat : uint8_t { + /** + * Tagged Image File Format (TIFF) + */ + TIFF = 0, + + /** + * Netpbm color image format (Portable Pixel Map) + */ + PPM = 1, + }; + /** * Minimum time that automation waits between applying a settings object and advancing * to the next test case. Specified in seconds. @@ -82,6 +98,11 @@ class UTILS_PUBLIC AutomationEngine { * If true, the tick function writes out a settings JSON file before advancing. */ bool exportSettings = false; + + /** + * Which image format will be used for exporting screenshots. + */ + ExportFormat exportFormat = ExportFormat::TIFF; }; /** @@ -224,6 +245,9 @@ class UTILS_PUBLIC AutomationEngine { */ static void exportSettings(const Settings& settings, const char* filename); + static void exportScreenshot(View* view, Renderer* renderer, std::string filename, + bool autoclose, AutomationEngine* automationEngine); + Options getOptions() const { return mOptions; } bool isRunning() const { return mIsRunning; } size_t currentTest() const { return mCurrentTest; } diff --git a/package/ios/libs/filament/include/viewer/Settings.h b/package/ios/libs/filament/include/viewer/Settings.h index 2094296b..b16ee8ae 100644 --- a/package/ios/libs/filament/include/viewer/Settings.h +++ b/package/ios/libs/filament/include/viewer/Settings.h @@ -51,6 +51,7 @@ struct Settings; struct ViewSettings; struct LightSettings; struct ViewerOptions; +struct DebugOptions; enum class ToneMapping : uint8_t { LINEAR = 0, @@ -88,6 +89,8 @@ void applySettings(Engine* engine, const LightSettings& settings, IndirectLight* const utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene, View* view); void applySettings(Engine* engine, const ViewerOptions& settings, Camera* camera, Skybox* skybox, Renderer* renderer); +void applySettings(Engine* engine, const DebugOptions& settings, + Renderer* renderer); // Creates a new ColorGrading object based on the given settings. UTILS_PUBLIC @@ -246,11 +249,16 @@ struct ViewerOptions { bool autoInstancingEnabled = false; }; +struct DebugOptions { + uint16_t skipFrames = 0; +}; + struct Settings { ViewSettings view; MaterialSettings material; LightSettings lighting; ViewerOptions viewer; + DebugOptions debug; }; } // namespace viewer diff --git a/package/package.json b/package/package.json index 352f7491..20f6ec43 100644 --- a/package/package.json +++ b/package/package.json @@ -35,7 +35,7 @@ "build": "bob build", "prepare": "git submodule update --init --recursive && bun patch-filament && bob build", "update-submodule": "git submodule update --remote --merge", - "patch-filament": "cd ../filament ; git apply ../package/filament_animator_feat.patch ; git apply ../package/filament_material_instance_getter.patch ; git apply ../package/filament_ios_simulator.patch ; cd ../package", + "patch-filament": "cd ../filament ; git apply ../package/filament_animator_feat.patch ; git apply ../package/filament_ios_simulator.patch ; git apply ../package/filament_depth_clip_mode.patch ; cd ../package", "build-filament": "bun patch-filament && scripts/build-filament.sh", "build-filament:release": "bun build-filament release", "build-filament:debug": "bun build-filament debug", diff --git a/package/scripts/BulletAndroid.mk b/package/scripts/BulletAndroid.mk new file mode 100644 index 00000000..d9d9cee8 --- /dev/null +++ b/package/scripts/BulletAndroid.mk @@ -0,0 +1,57 @@ +LOCAL_PATH := ../../.. + +include $(CLEAR_VARS) + +# Disable profiling to avoid thread-local storage issues with NDK 27 +# Enable emulated TLS for all architectures to avoid __tls_get_addr linker errors +LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -DBT_THREADSAFE -DSCE_PFX_USE_SIMD_VECTORMATH -DBT_NO_PROFILE +# TODO: When we bump the mind SDK to >= 29, we can remove -femulated-tls +LOCAL_CFLAGS += -femulated-tls + +LOCAL_CFLAGS += -ffast-math -funsafe-math-optimizations + +# apply this to disable optimization +# TARGET_CFLAGS := $(TARGET_CFLAGS) -O0 + +# apply these 2 to turn on assembly output (*.c/*.cpp to *.s file) +#compile-cpp-source = $(eval $(call ev-compile-cpp-source,$1,$(1:%$(LOCAL_CPP_EXTENSION)=%.s))) +#TARGET_CFLAGS := $(TARGET_CFLAGS) -S + +# Enable or disable NEON. Don't forget to apply, or not apply, -mfpu=neon and -mfloat-abi=softfp +# flags in addition, e.g., if this is true both of those need to be included in LOCAL_CFLAGS +# to avoid the possibility that ndk-build will "forget" to add them on some files +LOCAL_ARM_NEON := true + +# Only apply ARM32-specific flags for armeabi-v7a (NDK 27+ compatibility) +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) + LOCAL_CFLAGS += -mfpu=neon -mfloat-abi=softfp +endif + +TARGET_CFLAGS := $(filter-out -ffpu=vfp,$(TARGET_CFLAGS)) + +# setup to build static library +LOCAL_MODULE := libBullet + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/src + +#find all the file recursively under jni/ +FILE_LIST := $(wildcard \ + $(LOCAL_PATH)/src/LinearMath/*.cpp \ + $(LOCAL_PATH)/src/Bullet3Common/*.cpp \ + $(LOCAL_PATH)/src/BulletCollision/BroadphaseCollision/*.cpp \ + $(LOCAL_PATH)/src/BulletCollision/CollisionDispatch/*.cpp \ + $(LOCAL_PATH)/src/BulletCollision/CollisionShapes/*.cpp \ + $(LOCAL_PATH)/src/BulletCollision/NarrowPhaseCollision/*.cpp \ + $(LOCAL_PATH)/src/BulletDynamics/ConstraintSolver/*.cpp \ + $(LOCAL_PATH)/src/BulletDynamics/Dynamics/*.cpp \ + $(LOCAL_PATH)/src/BulletDynamics/Featherstone/*.cpp \ + $(LOCAL_PATH)/src/BulletDynamics/MLCPSolvers/*.cpp \ + $(LOCAL_PATH)/src/BulletDynamics/Vehicle/*.cpp \ + $(LOCAL_PATH)/src/BulletDynamics/Character/*.cpp \ + $(LOCAL_PATH)/src/BulletSoftBody/*.cpp \ + $(LOCAL_PATH)/src/BulletInverseDynamics/*.cpp \ + $(LOCAL_PATH)/src/BulletInverseDynamics/details/*.cpp \ + ) +LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%) + +include $(BUILD_STATIC_LIBRARY) diff --git a/package/scripts/build-bullet3.sh b/package/scripts/build-bullet3.sh index f2c025a2..9eec6f94 100755 --- a/package/scripts/build-bullet3.sh +++ b/package/scripts/build-bullet3.sh @@ -47,11 +47,18 @@ ANDROID_NDK_VERSION="$(grep '^Filament_ndkversion' ./android/gradle.properties | echo "Using target SDK: $TARGET_SDK" echo "Using NDK version: $ANDROID_NDK_VERSION" -# We need to copy over the updated BulletAndroid.mk file, since theirs is outdated +# We need to copy over the updated Bullet Android build files for NDK 27 compatibility cp -f scripts/BulletAndroidApplication.mk ../bullet3/build3/Android/jni/Application.mk +cp -f scripts/BulletAndroid.mk ../bullet3/build3/Android/jni/Android.mk # Change the {PLATFORM_NAME} to the actual platform value from gradle.properties sed -i '' "s/{PLATFORM_NAME}/$TARGET_SDK/g" ../bullet3/build3/Android/jni/Application.mk +# Temporarily rename VERSION file to avoid conflicts with C++ standard library headers +# (NDK 27+ includes header which conflicts with bullet3's VERSION file) +if [ -f ../bullet3/VERSION ]; then + mv ../bullet3/VERSION ../bullet3/VERSION.tmp +fi + cd ../bullet3/build3/Android/jni # Build the Bullet3 library $ANDROID_HOME/ndk/$ANDROID_NDK_VERSION/ndk-build @@ -61,7 +68,12 @@ cp -rf ../obj/local/* ../../../../package/android/libs/bullet3/lib # Clean all changes in bullet3 (the build files are not under gitignore) git checkout . # Discard all uncommitted changes -rm -rf ../obj/ # Remove untracked files and directories +rm -rf ../obj/ # Remove untracked files and directories + +# Restore VERSION file if it was renamed +if [ -f ../../../../bullet3/VERSION.tmp ]; then + mv ../../../../bullet3/VERSION.tmp ../../../../bullet3/VERSION +fi # Remove the objs folder cd ../../../../package/android/libs/bullet3/lib diff --git a/package/scripts/build-filament.sh b/package/scripts/build-filament.sh index 8a3cfef9..0c0ba51b 100755 --- a/package/scripts/build-filament.sh +++ b/package/scripts/build-filament.sh @@ -64,6 +64,11 @@ if [ "$skip_ios" = false ]; then cp -rf out/ios-$target/filament ../package/ios/libs # Rename math.h file to avoid conflicts with system header (the header doesn't seem to be used anywhere). mv ../package/ios/libs/filament/include/gltfio/math.h ../package/ios/libs/filament/include/gltfio/gltfio-math.h + + # Copy private backend headers needed for JNI initialization + echo "Copying Filament private backend headers..." + mkdir -p ../package/ios/libs/filament/include/private/backend + cp filament/backend/include/private/backend/VirtualMachineEnv.h ../package/ios/libs/filament/include/private/backend/ fi if [ "$skip_android" = false ]; then @@ -81,6 +86,11 @@ if [ "$skip_android" = false ]; then rm -rf ../package/android/libs/filament mkdir -p ../package/android/libs/filament cp -rf out/android-$target/filament ../package/android/libs + + # Copy private backend headers needed for JNI initialization + echo "Copying Filament private backend headers..." + mkdir -p ../package/android/libs/filament/include/private/backend + cp filament/backend/include/private/backend/VirtualMachineEnv.h ../package/android/libs/filament/include/private/backend/ fi echo "Done!"