From a8fda9b4d079b8bc17f49b9a328cedfd099e7b7e Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 19 Mar 2024 11:11:44 -0700 Subject: [PATCH 01/28] Make PBR Neutral invertible (#7677) Based on model-viewer's change at https://github.com/google/model-viewer/pull/4716 --- filament/src/ToneMapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/src/ToneMapper.cpp b/filament/src/ToneMapper.cpp index 2afa58f4be3..9c5a191210c 100644 --- a/filament/src/ToneMapper.cpp +++ b/filament/src/ToneMapper.cpp @@ -253,7 +253,7 @@ float3 PBRNeutralToneMapper::operator()(math::float3 color) const noexcept { color *= newPeak / peak; float g = 1.0f - 1.0f / (desaturation * (peak - newPeak) + 1.0f); - return mix(color, float3(1.0f), g); + return mix(color, float3(newPeak), g); } //------------------------------------------------------------------------------ From 93b15dac87d255ac6c6b66ddaf9aa00ea5a55a3f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 14 Mar 2024 22:20:28 -0700 Subject: [PATCH 02/28] The type of geometry of a renderable can now be specified - dynamic (default) no restriction apply - static bounds: bounds and world transform can't be changed - static: additionaly morphing/skinning and vertex/index buffers are immutable. This will allow some optimizations in the future. Currently, we just store the type but don't do anything with it. --- .../src/main/cpp/RenderableManager.cpp | 8 +++ .../android/filament/RenderableManager.java | 27 +++++++++ filament/include/filament/RenderableManager.h | 23 +++++++- filament/src/RenderableManager.cpp | 2 +- filament/src/components/RenderableManager.cpp | 22 ++++++- filament/src/components/RenderableManager.h | 57 ++++++++++++++----- web/filament-js/jsbindings.cpp | 4 ++ 7 files changed, 125 insertions(+), 18 deletions(-) diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index 2713dffa0b4..4e586179baa 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -104,6 +104,14 @@ Java_com_google_android_filament_RenderableManager_nBuilderGeometry__JIIJJIIII(J (size_t) count); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nBuilderGeometryType(JNIEnv*, jclass, + jlong nativeBuilder, int type) { + RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; + builder->geometryType((RenderableManager::Builder::GeometryType)type); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_RenderableManager_nBuilderMaterial(JNIEnv*, jclass, diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java index a3c8f1ff1c2..f7a6319d7a8 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java @@ -175,6 +175,32 @@ public Builder geometry(@IntRange(from = 0) int index, @NonNull PrimitiveType ty return this; } + /** + * Type of geometry for a Renderable + */ + public enum GeometryType { + /** dynamic gemoetry has no restriction */ + DYNAMIC, + /** bounds and world space transform are immutable */ + STATIC_BOUNDS, + /** skinning/morphing not allowed and Vertex/IndexBuffer immutables */ + STATIC + } + + /** + * Specify whether this renderable has static bounds. In this context his means that + * the renderable's bounding box cannot change and that the renderable's transform is + * assumed immutable. Changing the renderable's transform via the TransformManager + * can lead to corrupted graphics. Note that skinning and morphing are not forbidden. + * Disabled by default. + * @param enable whether this renderable has static bounds. false by default. + */ + @NonNull + public Builder geometryType(GeometryType type) { + nBuilderGeometryType(mNativeBuilder, type.ordinal()); + return this; + } + /** * Binds a material instance to the specified primitive. * @@ -964,6 +990,7 @@ public long getNativeObject() { private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer); private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int count); private static native void nBuilderGeometry(long nativeBuilder, int index, int value, long nativeVertexBuffer, long nativeIndexBuffer, int offset, int minIndex, int maxIndex, int count); + private static native void nBuilderGeometryType(long nativeBuilder, int type); private static native void nBuilderMaterial(long nativeBuilder, int index, long nativeMaterialInstance); private static native void nBuilderBlendOrder(long nativeBuilder, int index, int blendOrder); private static native void nBuilderGlobalBlendOrderEnabled(long nativeBuilder, int index, boolean enabled); diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index bec39e4af05..bb50b7d1db8 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -156,6 +156,15 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { */ static constexpr uint8_t DEFAULT_CHANNEL = 2u; + /** + * Type of geometry for a Renderable + */ + enum class GeometryType : uint8_t { + DYNAMIC, //!< dynamic gemoetry has no restriction + STATIC_BOUNDS, //!< bounds and world space transform are immutable + STATIC //!< skinning/morphing not allowed and Vertex/IndexBuffer immutables + }; + /** * Creates a builder for renderable components. * @@ -204,6 +213,17 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { VertexBuffer* UTILS_NONNULL vertices, IndexBuffer* UTILS_NONNULL indices) noexcept; //!< \overload + + /** + * Specify the type of geometry for this renderable. DYNAMIC geometry has no restriction, + * STATIC_BOUNDS geometry means that both the bounds and the world-space transform of the + * 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. + */ + Builder& geometryType(GeometryType type) noexcept; + /** * Binds a material instance to the specified primitive. * @@ -603,11 +623,12 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { /** * Changes the bounding box used for frustum culling. + * The renderable must not have staticGeometry enabled. * * \see Builder::boundingBox() * \see RenderableManager::getAxisAlignedBoundingBox() */ - void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept; + void setAxisAlignedBoundingBox(Instance instance, const Box& aabb); /** * Changes the visibility bits. diff --git a/filament/src/RenderableManager.cpp b/filament/src/RenderableManager.cpp index 71a83bf544c..133dd817c2c 100644 --- a/filament/src/RenderableManager.cpp +++ b/filament/src/RenderableManager.cpp @@ -56,7 +56,7 @@ void RenderableManager::destroy(utils::Entity e) noexcept { return downcast(this)->destroy(e); } -void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept { +void RenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) { downcast(this)->setAxisAlignedBoundingBox(instance, aabb); } diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index a7f3fee3f21..3bbd85597ce 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -58,6 +58,7 @@ struct RenderableManager::BuilderDetails { bool mScreenSpaceContactShadows : 1; bool mSkinningBufferMode : 1; bool mFogEnabled : 1; + RenderableManager::Builder::GeometryType mGeometryType : 2; size_t mSkinningBoneCount = 0; size_t mMorphTargetCount = 0; Bone const* mUserBones = nullptr; @@ -75,7 +76,9 @@ struct RenderableManager::BuilderDetails { explicit BuilderDetails(size_t count) : mEntries(count), mCulling(true), mCastShadows(false), mReceiveShadows(true), mScreenSpaceContactShadows(false), - mSkinningBufferMode(false), mFogEnabled(true), mBonePairs() { + mSkinningBufferMode(false), mFogEnabled(true), + mGeometryType(RenderableManager::Builder::GeometryType::DYNAMIC), + mBonePairs() { } // this is only needed for the explicit instantiation below BuilderDetails() = default; @@ -121,6 +124,11 @@ RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index, return *this; } +RenderableManager::Builder& RenderableManager::Builder::geometryType(GeometryType type) noexcept { + mImpl->mGeometryType = type; + return *this; +} + RenderableManager::Builder& RenderableManager::Builder::material(size_t index, MaterialInstance const* materialInstance) noexcept { if (index < mImpl->mEntries.size()) { @@ -388,11 +396,21 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng ASSERT_PRECONDITION(mImpl->mSkinningBoneCount <= CONFIG_MAX_BONE_COUNT, "bone count > %u", CONFIG_MAX_BONE_COUNT); + ASSERT_PRECONDITION(mImpl->mInstanceCount <= CONFIG_MAX_INSTANCES || !mImpl->mInstanceBuffer, "instance count is %zu, but instance count is limited to CONFIG_MAX_INSTANCES (%zu) " "instances when supplying transforms via an InstanceBuffer.", mImpl->mInstanceCount, CONFIG_MAX_INSTANCES); + + if (mImpl->mGeometryType == GeometryType::STATIC) { + ASSERT_PRECONDITION(mImpl->mSkinningBoneCount > 0, + "Skinning can't be used with STATIC geometry"); + + ASSERT_PRECONDITION(mImpl->mMorphTargetCount > 0, + "Morphing can't be used with STATIC geometry"); + } + if (mImpl->mInstanceBuffer) { size_t const bufferInstanceCount = mImpl->mInstanceBuffer->mInstanceCount; ASSERT_PRECONDITION(mImpl->mInstanceCount <= bufferInstanceCount, @@ -519,6 +537,8 @@ void FRenderableManager::create( setSkinning(ci, false); setMorphing(ci, builder->mMorphTargetCount); setFogEnabled(ci, builder->mFogEnabled); + // do this after calling setAxisAlignedBoundingBox + static_cast(mManager[ci].visibility).geometryType = builder->mGeometryType; mManager[ci].channels = builder->mLightChannels; InstancesInfo& instances = manager[ci].instances; diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 746c46dbeee..920dd02a88a 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -22,22 +22,30 @@ #include "HwRenderPrimitiveFactory.h" #include "UniformBuffer.h" -#include "backend/DriverApiForward.h" - -#include +#include
#include #include -#include
- #include +#include +#include + +#include #include +#include #include #include #include +#include + +#include + +#include +#include + namespace filament { class FBufferObject; @@ -49,9 +57,12 @@ class FSkinningBuffer; class FVertexBuffer; class FTexture; +class MorphTargetBuffer; + class FRenderableManager : public RenderableManager { public: using Instance = RenderableManager::Instance; + using GeometryType = RenderableManager::Builder::GeometryType; // TODO: consider renaming, this pertains to material variants, not strictly visibility. struct Visibility { @@ -60,11 +71,13 @@ class FRenderableManager : public RenderableManager { bool castShadows : 1; bool receiveShadows : 1; bool culling : 1; + bool skinning : 1; bool morphing : 1; bool screenSpaceContactShadows : 1; bool reversedWindingOrder : 1; bool fog : 1; + GeometryType geometryType : 2; }; static_assert(sizeof(Visibility) == sizeof(uint16_t), "Visibility should be 16 bits"); @@ -115,7 +128,7 @@ class FRenderableManager : public RenderableManager { void destroy(utils::Entity e) noexcept; - inline void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept; + inline void setAxisAlignedBoundingBox(Instance instance, const Box& aabb); inline void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept; @@ -136,13 +149,13 @@ class FRenderableManager : public RenderableManager { inline void setPrimitives(Instance instance, utils::Slice const& primitives) noexcept; - inline void setSkinning(Instance instance, bool enable) noexcept; + inline void setSkinning(Instance instance, bool enable); void setBones(Instance instance, Bone const* transforms, size_t boneCount, size_t offset = 0); void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount, size_t offset = 0); void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer, size_t count, size_t offset); - inline void setMorphing(Instance instance, bool enable) noexcept; + inline void setMorphing(Instance instance, bool enable); void setMorphWeights(Instance instance, float const* weights, size_t count, size_t offset); void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count); @@ -295,8 +308,12 @@ class FRenderableManager : public RenderableManager { FILAMENT_DOWNCAST(RenderableManager) -void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept { +void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& aabb) { if (instance) { + ASSERT_PRECONDITION( + static_cast( + mManager[instance].visibility).geometryType != GeometryType::DYNAMIC, + "This renderable has staticBounds enabled; its AABB cannot change."); mManager[instance].aabb = aabb; } } @@ -368,16 +385,26 @@ bool FRenderableManager::getFogEnabled(RenderableManager::Instance instance) con return getVisibility(instance).fog; } -void FRenderableManager::setSkinning(Instance instance, bool enable) noexcept { +void FRenderableManager::setSkinning(Instance instance, bool enable) { if (instance) { Visibility& visibility = mManager[instance].visibility; + + ASSERT_PRECONDITION( + visibility.geometryType != GeometryType::STATIC || !enable, + "Skinning can't be used with STATIC geometry"); + visibility.skinning = enable; } } -void FRenderableManager::setMorphing(Instance instance, bool enable) noexcept { +void FRenderableManager::setMorphing(Instance instance, bool enable) { if (instance) { Visibility& visibility = mManager[instance].visibility; + + ASSERT_PRECONDITION( + visibility.geometryType != GeometryType::STATIC || !enable, + "Morphing can't be used with STATIC geometry"); + visibility.morphing = enable; } } @@ -446,22 +473,22 @@ FRenderableManager::getInstancesInfo(Instance instance) const noexcept { } utils::Slice const& FRenderableManager::getRenderPrimitives( - Instance instance, uint8_t level) const noexcept { + Instance instance, UTILS_UNUSED uint8_t level) const noexcept { return mManager[instance].primitives; } utils::Slice& FRenderableManager::getRenderPrimitives( - Instance instance, uint8_t level) noexcept { + Instance instance, UTILS_UNUSED uint8_t level) noexcept { return mManager[instance].primitives; } utils::Slice const& FRenderableManager::getMorphTargets( - Instance instance, uint8_t level) const noexcept { + Instance instance, UTILS_UNUSED uint8_t level) const noexcept { return mManager[instance].morphTargets; } utils::Slice& FRenderableManager::getMorphTargets( - Instance instance, uint8_t level) noexcept { + Instance instance, UTILS_UNUSED uint8_t level) noexcept { return mManager[instance].morphTargets; } diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index bf8e860b5b6..fed31ce1407 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -938,6 +938,10 @@ class_("RenderableManager$Builder") size_t count), { return &builder->geometry(index, type, vertices, indices, offset, minIndex, maxIndex, count); }) + .BUILDER_FUNCTION("geometryType", RenderableBuilder, (RenderableBuilder* builder, + RenderableManager::Builder::GeometryType type), { + return &builder->geometryType(type); }) + .BUILDER_FUNCTION("material", RenderableBuilder, (RenderableBuilder* builder, size_t index, MaterialInstance* mi), { return &builder->material(index, mi); }) From 29bb60cd94978835787207e4bbce3c58b64bd32e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 19 Mar 2024 13:12:04 -0700 Subject: [PATCH 03/28] fix typo that caused a wrong assertion --- filament/src/components/RenderableManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 920dd02a88a..003026baa0d 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -312,7 +312,7 @@ void FRenderableManager::setAxisAlignedBoundingBox(Instance instance, const Box& if (instance) { ASSERT_PRECONDITION( static_cast( - mManager[instance].visibility).geometryType != GeometryType::DYNAMIC, + mManager[instance].visibility).geometryType == GeometryType::DYNAMIC, "This renderable has staticBounds enabled; its AABB cannot change."); mManager[instance].aabb = aabb; } From 1ab223c965ac7e95330113cb7a52f1fc7cdb5afc Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 19 Mar 2024 13:59:52 -0700 Subject: [PATCH 04/28] vk: minor fixes (#7682) - Add description to a few debug options - Set correct usage flag for blit-src/dest images - Correctly initialize a debug-only field in VulkanProgram --- filament/backend/src/vulkan/VulkanConstants.h | 13 +++++++++---- filament/backend/src/vulkan/VulkanHandles.h | 6 +++++- filament/backend/src/vulkan/VulkanTexture.cpp | 10 +++++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h index 5856e391641..b4974950ef5 100644 --- a/filament/backend/src/vulkan/VulkanConstants.h +++ b/filament/backend/src/vulkan/VulkanConstants.h @@ -43,9 +43,12 @@ // Enables Android systrace #define FVK_DEBUG_SYSTRACE 0x00000001 -// Group markers are used to denote collection of GPU commands. It is typically at the granualarity -// of a renderpass. -#define FVK_DEBUG_GROUP_MARKERS 0x00000002 +// Group markers are used to denote collections of GPU commands. It is typically at the +// granualarity of a renderpass. You can enable this along with FVK_DEBUG_DEBUG_UTILS to take +// advantage of vkCmdBegin/EndDebugUtilsLabelEXT. You can also just enable this with +// FVK_DEBUG_PRINT_GROUP_MARKERS to print the current marker to stdout. +#define FVK_DEBUG_GROUP_MARKERS 0x00000002 + #define FVK_DEBUG_TEXTURE 0x00000004 #define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008 #define FVK_DEBUG_COMMAND_BUFFER 0x00000010 @@ -63,9 +66,11 @@ // Enable the debug utils extension if it is available. #define FVK_DEBUG_DEBUG_UTILS 0x00008000 +// Use this to debug potential Handle/Resource leakage. It will print out reference counts for all +// the currently active resources. #define FVK_DEBUG_RESOURCE_LEAK 0x00010000 -// Usefaul default combinations +// Useful default combinations #define FVK_DEBUG_EVERYTHING 0xFFFFFFFF #define FVK_DEBUG_PERFORMANCE \ FVK_DEBUG_SYSTRACE diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 7c65915d51f..f6158c77ae9 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -77,7 +77,11 @@ struct VulkanProgram : public HwProgram, VulkanResource { struct PipelineInfo { PipelineInfo() - : bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {} + : bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) +#if FVK_ENABLED_DEBUG_SAMPLER_NAME + , bindingToName(MAX_SAMPLER_COUNT, "") +#endif + {} // This bitset maps to each of the sampler in the sampler groups associated with this // program, and whether each sampler is used in which shader (i.e. vert, frag, compute). diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index e595f3bec93..f611f40aac7 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -18,10 +18,10 @@ #include "VulkanTexture.h" #include "VulkanUtility.h" +#include +#include #include -#include "DataReshaper.h" - #include using namespace bluevk; @@ -100,9 +100,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, // Filament expects blit() to work with any texture, so we almost always set these usage flags. // TODO: investigate performance implications of setting these flags. - const VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT | + constexpr VkImageUsageFlags blittable = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::BLIT_SRC))) { + imageInfo.usage |= blittable; + } + if (any(usage & TextureUsage::SAMPLEABLE)) { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) From dfa821d35167d23036697bda1b80f8257b8dd9ee Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 19 Mar 2024 15:38:52 -0700 Subject: [PATCH 05/28] vk: fix broken gltf renderings (#7680) This is due to color attachments being set to store=discard when they are multi-sampled. It's unclear why that condition exists. For now, removing it will fix the rendering issues with transparent object + MSAA. We'll keep it as such until an issue surfaces. Fixes #7674 --- filament/backend/src/vulkan/VulkanFboCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/vulkan/VulkanFboCache.cpp b/filament/backend/src/vulkan/VulkanFboCache.cpp index 009ca0273ca..f4b222b1545 100644 --- a/filament/backend/src/vulkan/VulkanFboCache.cpp +++ b/filament/backend/src/vulkan/VulkanFboCache.cpp @@ -239,7 +239,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept { .format = config.colorFormat[i], .samples = (VkSampleCountFlagBits) config.samples, .loadOp = clear ? kClear : (discard ? kDontCare : kKeep), - .storeOp = config.samples == 1 ? kEnableStore : kDisableStore, + .storeOp = kEnableStore, .stencilLoadOp = kDontCare, .stencilStoreOp = kDisableStore, .initialLayout = ((!discard && config.initialColorLayoutMask & (1 << i)) || clear) From e3db39105f1c63f3ac29ea76217256fe7e91bdfe Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 19 Mar 2024 16:14:45 -0700 Subject: [PATCH 06/28] iOS: fix gltf-viewer crash while destruction ResourceLoader (#7684) --- ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm | 8 ++++---- libs/gltfio/include/gltfio/ResourceLoader.h | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm b/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm index 2136fb4ed0c..20448ee63cc 100644 --- a/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm +++ b/ios/samples/gltf-viewer/gltf-viewer/FILModelView.mm @@ -275,10 +275,6 @@ - (void)render { - (void)dealloc { [self destroyModel]; - delete _manipulator; - delete _stbDecoder; - delete _ktxDecoder; - _materialProvider->destroyMaterials(); delete _materialProvider; auto* ncm = _assetLoader->getNames(); @@ -286,6 +282,10 @@ - (void)dealloc { AssetLoader::destroy(&_assetLoader); delete _resourceLoader; + delete _manipulator; + delete _stbDecoder; + delete _ktxDecoder; + _engine->destroy(_swapChain); _engine->destroy(_view); EntityManager::get().destroy(_entities.camera); diff --git a/libs/gltfio/include/gltfio/ResourceLoader.h b/libs/gltfio/include/gltfio/ResourceLoader.h index acccc9f86fc..d222871bd7e 100644 --- a/libs/gltfio/include/gltfio/ResourceLoader.h +++ b/libs/gltfio/include/gltfio/ResourceLoader.h @@ -93,7 +93,8 @@ class UTILS_PUBLIC ResourceLoader { /** * Register a plugin that can consume PNG / JPEG content and produce filament::Texture objects. * - * Destruction of the given provider is the client's responsibility. + * Destruction of the given provider is the client's responsibility and must be done after the + * destruction of this ResourceLoader. */ void addTextureProvider(const char* mimeType, TextureProvider* provider); From 435969f56568644df2fa934b02d3f2a02879adac Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 20 Mar 2024 14:57:42 -0700 Subject: [PATCH 07/28] attempt to detect buffer overflows in Texture::setImage() We verify that the buffer given to setImage() is at least as large as needed for the given region to transfer; at least based on the size given. This might help catch b/330407429. BUGS=330407429 --- filament/backend/src/opengl/OpenGLDriver.cpp | 4 +- filament/src/details/Texture.cpp | 46 +++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 2d5c45bed18..4dca55b5de6 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -2462,7 +2462,9 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level, size_t const stride = p.stride ? p.stride : width; size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1); size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment); - void const* const buffer = static_cast(p.buffer) + p.left * bpp + bpr * p.top; + size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride" + void const* const buffer = static_cast(p.buffer) + + bpp* p.left + bpr * p.top + bpl * 0; // TODO: PBD should have a p.depth switch (t->target) { case SamplerType::SAMPLER_EXTERNAL: diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 7a0754ac609..e47f7251b23 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -25,14 +25,33 @@ #include +#include +#include + #include #include #include #include +#include +#include +#include + +#include +#include +#include +#include #include #include +#include +#include +#include +#include + +#include +#include + using namespace utils; namespace filament { @@ -246,20 +265,20 @@ size_t FTexture::getDepth(size_t level) const noexcept { void FTexture::setImage(FEngine& engine, size_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, - FTexture::PixelBufferDescriptor&& buffer) const { + FTexture::PixelBufferDescriptor&& p) const { if (UTILS_UNLIKELY(!engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1))) { - ASSERT_PRECONDITION(buffer.stride == 0 || buffer.stride == width, + ASSERT_PRECONDITION(p.stride == 0 || p.stride == width, "PixelBufferDescriptor stride must be 0 (or width) at FEATURE_LEVEL_0"); } // this should have been validated already assert_invariant(isTextureFormatSupported(engine, mFormat)); - ASSERT_PRECONDITION(buffer.type == PixelDataType::COMPRESSED || - validatePixelFormatAndType(mFormat, buffer.format, buffer.type), + ASSERT_PRECONDITION(p.type == PixelDataType::COMPRESSED || + validatePixelFormatAndType(mFormat, p.format, p.type), "The combination of internal format=%u and {format=%u, type=%u} is not supported.", - unsigned(mFormat), unsigned(buffer.format), unsigned(buffer.type)); + unsigned(mFormat), unsigned(p.format), unsigned(p.type)); ASSERT_PRECONDITION(!mStream, "setImage() called on a Stream texture."); @@ -283,7 +302,7 @@ void FTexture::setImage(FEngine& engine, size_t level, unsigned(yoffset), unsigned(height), unsigned(valueForLevel(level, mHeight)), unsigned(level)); - ASSERT_PRECONDITION(buffer.buffer, "Data buffer is nullptr."); + ASSERT_PRECONDITION(p.buffer, "Data buffer is nullptr."); uint32_t effectiveTextureDepthOrLayers; switch (mTarget) { @@ -311,8 +330,21 @@ void FTexture::setImage(FEngine& engine, size_t level, "zoffset (%u) + depth (%u) > texture depth (%u) at level (%u)", unsigned(zoffset), unsigned(depth), effectiveTextureDepthOrLayers, unsigned(level)); + using PBD = PixelBufferDescriptor; + size_t const stride = p.stride ? p.stride : width; + size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1); + size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment); + size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride" + // TODO: PBD should have a p.depth (# layers to skip) + ASSERT_PRECONDITION(bpp * p.left + bpr * p.top + bpl * (0 + depth) <= p.size, + "buffer overflow: (size=%lu, stride=%lu, left=%u, top=%u) smaller than specified region " + "{{%u,%u,%u},{%u,%u,%u)}}", + size_t(p.size), size_t(p.stride), unsigned(p.left), unsigned(p.top), + unsigned(xoffset), unsigned(yoffset), unsigned(zoffset), + unsigned(width), unsigned(height), unsigned(depth)); + engine.getDriverApi().update3DImage(mHandle, - uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(buffer)); + uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(p)); } // deprecated From 59890ac85a9daf405de548f69f3e16b9b5cd2aae Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 20 Mar 2024 13:28:09 -0700 Subject: [PATCH 08/28] PlatformEGL::createSwapChain never returns a nullptr anymore in case the swapchain creation fails, it will now return a swapchain with an EGL_NO_SURFACE handle. this will avoid having to nullptr check the pointer in various places and will revert to the previous behavior on failure. FIXES=[329659681] --- .../backend/platforms/OpenGLPlatform.h | 2 +- filament/backend/src/opengl/OpenGLDriver.cpp | 4 + .../src/opengl/platforms/PlatformEGL.cpp | 76 +++++++++---------- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/filament/backend/include/backend/platforms/OpenGLPlatform.h b/filament/backend/include/backend/platforms/OpenGLPlatform.h index 565a4641cbc..dec6f47ba74 100644 --- a/filament/backend/include/backend/platforms/OpenGLPlatform.h +++ b/filament/backend/include/backend/platforms/OpenGLPlatform.h @@ -89,7 +89,7 @@ class OpenGLPlatform : public Platform { * @return The driver's SwapChain object. * */ - virtual SwapChain* UTILS_NONNULL createSwapChain( + virtual SwapChain* UTILS_NULLABLE createSwapChain( void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0; /** diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 4dca55b5de6..9113f094f9c 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -1461,6 +1461,8 @@ void OpenGLDriver::createSwapChainR(Handle sch, void* nativeWindow, GLSwapChain* sc = handle_cast(sch); sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags); + + // note: in practice this should never happen on Android ASSERT_POSTCONDITION(sc->swapChain, "createSwapChain(%p, 0x%lx) failed. See logs for details.", nativeWindow, flags); @@ -1478,6 +1480,8 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle sch, GLSwapChain* sc = handle_cast(sch); sc->swapChain = mPlatform.createSwapChain(width, height, flags); + + // note: in practice this should never happen on Android ASSERT_POSTCONDITION(sc->swapChain, "createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.", width, height, flags); diff --git a/filament/backend/src/opengl/platforms/PlatformEGL.cpp b/filament/backend/src/opengl/platforms/PlatformEGL.cpp index fcf4b9c4922..b041473839f 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGL.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGL.cpp @@ -454,20 +454,7 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept { Platform::SwapChain* PlatformEGL::createSwapChain( void* nativeWindow, uint64_t flags) noexcept { - EGLConfig config = EGL_NO_CONFIG_KHR; - if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) { - config = findSwapChainConfig(flags, true, false); - } else { - config = mEGLConfig; - } - - if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) { - // error already logged - return nullptr; - } - Config attribs; - if (ext.egl.KHR_gl_colorspace) { if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) { attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR; @@ -484,16 +471,27 @@ Platform::SwapChain* PlatformEGL::createSwapChain( flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT; } - EGLSurface sur = eglCreateWindowSurface(mEGLDisplay, config, - (EGLNativeWindowType)nativeWindow, attribs.data()); - - if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) { - logEglError("PlatformEGL::createSwapChain: eglCreateWindowSurface"); - return nullptr; + EGLConfig config = EGL_NO_CONFIG_KHR; + if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) { + config = findSwapChainConfig(flags, true, false); + } else { + config = mEGLConfig; } - // this is not fatal - eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); + EGLSurface sur = EGL_NO_SURFACE; + if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) { + sur = eglCreateWindowSurface(mEGLDisplay, config, + (EGLNativeWindowType)nativeWindow, attribs.data()); + + if (UTILS_LIKELY(sur != EGL_NO_SURFACE)) { + // this is not fatal + eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); + } else { + logEglError("PlatformEGL::createSwapChain: eglCreateWindowSurface"); + } + } else { + // error already logged + } SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({ .sur = sur, @@ -508,18 +506,6 @@ Platform::SwapChain* PlatformEGL::createSwapChain( Platform::SwapChain* PlatformEGL::createSwapChain( uint32_t width, uint32_t height, uint64_t flags) noexcept { - EGLConfig config = EGL_NO_CONFIG_KHR; - if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) { - config = findSwapChainConfig(flags, false, true); - } else { - config = mEGLConfig; - } - - if (UTILS_UNLIKELY(config == EGL_NO_CONFIG_KHR)) { - // error already logged - return nullptr; - } - Config attribs = { { EGL_WIDTH, EGLint(width) }, { EGL_HEIGHT, EGLint(height) }, @@ -541,17 +527,28 @@ Platform::SwapChain* PlatformEGL::createSwapChain( flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT; } - EGLSurface sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data()); + EGLConfig config = EGL_NO_CONFIG_KHR; + if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) { + config = findSwapChainConfig(flags, true, false); + } else { + config = mEGLConfig; + } - if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) { - logEglError("PlatformEGL::createSwapChain: eglCreatePbufferSurface"); - return nullptr; + EGLSurface sur = EGL_NO_SURFACE; + if (UTILS_LIKELY(config != EGL_NO_CONFIG_KHR)) { + sur = eglCreatePbufferSurface(mEGLDisplay, config, attribs.data()); + if (UTILS_UNLIKELY(sur == EGL_NO_SURFACE)) { + logEglError("PlatformEGL::createSwapChain: eglCreatePbufferSurface"); + } + } else { + // error already logged } SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({ .sur = sur, .attribs = std::move(attribs), - .config = config + .config = config, + .flags = flags }); return sc; } @@ -593,6 +590,9 @@ void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain, utils::Invocable preContextChange, utils::Invocable postContextChange) noexcept { + assert_invariant(drawSwapChain); + assert_invariant(readSwapChain); + ContextType type = ContextType::UNPROTECTED; if (ext.egl.EXT_protected_content) { bool const swapChainProtected = isSwapChainProtected(drawSwapChain); From d5fe9e236fbdb0ff18603227b88e867cde847e90 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Mon, 11 Mar 2024 14:08:42 -0700 Subject: [PATCH 09/28] Allow rendering thread to pause This PR adds a new `pause()` option to the `Engine` `Builder` and a new function `setPaused()` to the `Engine`. While paused, the rendering thread will pause indefinitely for commands as if none are available. As soon as the rendering thread is unpaused, the commands are immediately executed. --- NEW_RELEASE_NOTES.md | 2 ++ .../filament-android/src/main/cpp/Engine.cpp | 13 ++++++++++++ .../com/google/android/filament/Engine.java | 21 +++++++++++++++++++ .../private/backend/CommandBufferQueue.h | 6 +++++- filament/backend/src/CommandBufferQueue.cpp | 16 +++++++++++--- filament/backend/test/BackendTest.cpp | 2 +- filament/backend/test/ComputeTest.cpp | 3 ++- filament/include/filament/Engine.h | 13 ++++++++++++ filament/src/Engine.cpp | 5 +++++ filament/src/details/Engine.cpp | 13 +++++++++++- filament/src/details/Engine.h | 2 ++ 11 files changed, 89 insertions(+), 7 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7e..77a7acc5f46 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,5 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut + +- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()` diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index 80409702c37..c9fa78dccd8 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -391,6 +391,13 @@ Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass, engine->flush(); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Engine_nSetPaused(JNIEnv*, jclass, + jlong nativeEngine, jboolean paused) { + Engine* engine = (Engine*) nativeEngine; + engine->setPaused(paused); +} + // Managers... extern "C" JNIEXPORT jlong JNICALL @@ -518,6 +525,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu builder->sharedContext((void*) sharedContext); } +extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderPaused( + JNIEnv*, jclass, jlong nativeBuilder, jboolean paused) { + Engine::Builder* builder = (Engine::Builder*) nativeBuilder; + builder->paused((bool) paused); +} + extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) { Engine::Builder* builder = (Engine::Builder*) nativeBuilder; diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index aee4c2b34aa..0a4c2135765 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -239,6 +239,18 @@ public Builder featureLevel(FeatureLevel featureLevel) { return this; } + /** + * Sets the initial paused state of the rendering thread. + * + * @param paused Whether to start the rendering thread paused. + * @return A reference to this Builder for chaining calls. + * @warning Experimental. + */ + public Builder paused(boolean paused) { + nSetBuilderPaused(mNativeBuilder, paused); + return this; + } + /** * Creates an instance of Engine * @@ -1189,6 +1201,13 @@ public void flush() { nFlush(getNativeObject()); } + /** + * Pause or resume the rendering thread. + * @warning Experimental. + */ + public void setPaused(boolean paused) { + nSetPaused(getNativeObject(), paused); + } @UsedByReflection("TextureHelper.java") public long getNativeObject() { @@ -1263,6 +1282,7 @@ private static void assertDestroy(boolean success) { private static native void nDestroyEntity(long nativeEngine, int entity); private static native void nFlushAndWait(long nativeEngine); private static native void nFlush(long nativeEngine); + private static native void nSetPaused(long nativeEngine, boolean paused); private static native long nGetTransformManager(long nativeEngine); private static native long nGetLightManager(long nativeEngine); private static native long nGetRenderableManager(long nativeEngine); @@ -1286,5 +1306,6 @@ private static native void nSetBuilderConfig(long nativeBuilder, long commandBuf long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge); private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal); private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext); + private static native void nSetBuilderPaused(long nativeBuilder, boolean paused); private static native long nBuilderBuild(long nativeBuilder); } diff --git a/filament/backend/include/private/backend/CommandBufferQueue.h b/filament/backend/include/private/backend/CommandBufferQueue.h index 28122452386..92bf7e1488c 100644 --- a/filament/backend/include/private/backend/CommandBufferQueue.h +++ b/filament/backend/include/private/backend/CommandBufferQueue.h @@ -50,12 +50,13 @@ class CommandBufferQueue { size_t mFreeSpace = 0; size_t mHighWatermark = 0; uint32_t mExitRequested = 0; + bool mPaused = false; static constexpr uint32_t EXIT_REQUESTED = 0x31415926; public: // requiredSize: guaranteed available space after flush() - CommandBufferQueue(size_t requiredSize, size_t bufferSize); + CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused); ~CommandBufferQueue(); CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; } @@ -80,6 +81,9 @@ class CommandBufferQueue { // returns from waitForCommands() immediately. void requestExit(); + // suspend or unsuspend the queue. + void setPaused(bool paused); + bool isExitRequested() const; }; diff --git a/filament/backend/src/CommandBufferQueue.cpp b/filament/backend/src/CommandBufferQueue.cpp index e3e5de045c8..9de07ed0e27 100644 --- a/filament/backend/src/CommandBufferQueue.cpp +++ b/filament/backend/src/CommandBufferQueue.cpp @@ -39,10 +39,11 @@ using namespace utils; namespace filament::backend { -CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize) +CommandBufferQueue::CommandBufferQueue(size_t requiredSize, size_t bufferSize, bool paused) : mRequiredSize((requiredSize + (CircularBuffer::getBlockSize() - 1u)) & ~(CircularBuffer::getBlockSize() -1u)), mCircularBuffer(bufferSize), - mFreeSpace(mCircularBuffer.size()) { + mFreeSpace(mCircularBuffer.size()), + mPaused(paused) { assert_invariant(mCircularBuffer.size() > requiredSize); } @@ -56,6 +57,15 @@ void CommandBufferQueue::requestExit() { mCondition.notify_one(); } +void CommandBufferQueue::setPaused(bool paused) { + if (paused) { + mPaused = true; + } else { + mPaused = false; + mCondition.notify_one(); + } +} + bool CommandBufferQueue::isExitRequested() const { std::lock_guard const lock(mLock); ASSERT_PRECONDITION( mExitRequested == 0 || mExitRequested == EXIT_REQUESTED, @@ -127,7 +137,7 @@ std::vector CommandBufferQueue::waitForCommands() con return std::move(mCommandBuffersToExecute); } std::unique_lock lock(mLock); - while (mCommandBuffersToExecute.empty() && !mExitRequested) { + while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) { mCondition.wait(lock); } diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index 0730236344e..5bb694c974b 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -51,7 +51,7 @@ void BackendTest::init(Backend backend, bool isMobilePlatform) { } BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, - CONFIG_COMMAND_BUFFERS_SIZE) { + CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) { initializeDriver(); } diff --git a/filament/backend/test/ComputeTest.cpp b/filament/backend/test/ComputeTest.cpp index 6ec640dbedd..5808c3b1825 100644 --- a/filament/backend/test/ComputeTest.cpp +++ b/filament/backend/test/ComputeTest.cpp @@ -46,7 +46,8 @@ void ComputeTest::init(Backend backend) { } ComputeTest::ComputeTest() - : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) { + : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE, + /*paused=*/false) { } ComputeTest::~ComputeTest() = default; diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 04d71259b7c..a702b3de263 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -402,6 +402,13 @@ class UTILS_PUBLIC Engine { */ Builder& featureLevel(FeatureLevel featureLevel) noexcept; + /** + * @param paused Whether to start the rendering thread paused. + * @return A reference to this Builder for chaining calls. + * @warning Experimental. + */ + Builder& paused(bool paused) noexcept; + #if UTILS_HAS_THREADING /** * Creates the filament Engine asynchronously. @@ -827,6 +834,12 @@ class UTILS_PUBLIC Engine { */ void flush(); + /** + * Pause or resume rendering thread. + * @warning Experimental. + */ + void setPaused(bool paused); + /** * Drains the user callback message queue and immediately execute all pending callbacks. * diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 380b7e55ade..ffb85ae70ad 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -295,6 +295,11 @@ utils::JobSystem& Engine::getJobSystem() noexcept { return downcast(this)->getJobSystem(); } +void Engine::setPaused(bool paused) { + ASSERT_PRECONDITION(UTILS_HAS_THREADING, "Pause is meant for multi-threaded platforms."); + downcast(this)->setPaused(paused); +} + DebugRegistry& Engine::getDebugRegistry() noexcept { return downcast(this)->getDebugRegistry(); } diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 97c22b90c10..c0d3f802fba 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -69,6 +69,7 @@ struct Engine::BuilderDetails { Engine::Config mConfig; FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1; void* mSharedContext = nullptr; + bool mPaused = false; static Config validateConfig(const Config* pConfig) noexcept; }; @@ -200,7 +201,8 @@ FEngine::FEngine(Engine::Builder const& builder) : mCameraManager(*this), mCommandBufferQueue( builder->mConfig.minCommandBufferSizeMB * MiB, - builder->mConfig.commandBufferSizeMB * MiB), + builder->mConfig.commandBufferSizeMB * MiB, + builder->mPaused), mPerRenderPassArena( "FEngine::mPerRenderPassAllocator", builder->mConfig.perRenderPassArenaSizeMB * MiB), @@ -1194,6 +1196,10 @@ void FEngine::destroy(FEngine* engine) { } } +void FEngine::setPaused(bool paused) { + mCommandBufferQueue.setPaused(paused); +} + Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept { FEngine::DriverApi& driver = const_cast(this)->getDriverApi(); return driver.getFeatureLevel(); @@ -1247,6 +1253,11 @@ Engine::Builder& Engine::Builder::sharedContext(void* sharedContext) noexcept { return *this; } +Engine::Builder& Engine::Builder::paused(bool paused) noexcept { + mImpl->mPaused = paused; + return *this; +} + #if UTILS_HAS_THREADING void Engine::Builder::build(Invocable&& callback) const { diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 002e4f25f97..ead937e644d 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -340,6 +340,8 @@ class FEngine : public Engine { void destroy(utils::Entity e); + void setPaused(bool paused); + void flushAndWait(); // flush the current buffer From 5389b37002e34b1a549fa9cc91f1c10ba28da717 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Mon, 18 Mar 2024 13:42:43 -0700 Subject: [PATCH 10/28] Incorporate feedback --- .../java/com/google/android/filament/Engine.java | 15 +++++++++++++-- filament/backend/src/CommandBufferQueue.cpp | 4 ++++ filament/include/filament/Engine.h | 14 ++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 0a4c2135765..1e430b51386 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -242,9 +242,11 @@ public Builder featureLevel(FeatureLevel featureLevel) { /** * Sets the initial paused state of the rendering thread. * + *

Warning: This is an experimental API. See {@link Engine#setPaused(boolean)} for + * caveats. + * * @param paused Whether to start the rendering thread paused. * @return A reference to this Builder for chaining calls. - * @warning Experimental. */ public Builder paused(boolean paused) { nSetBuilderPaused(mNativeBuilder, paused); @@ -1203,7 +1205,16 @@ public void flush() { /** * Pause or resume the rendering thread. - * @warning Experimental. + * + *

Warning: This is an experimental API. In particular, note the following caveats. + * + *

  • + * Buffer callbacks will never be called as long as the rendering thread is paused. + * Do not rely on a buffer callback to unpause the thread. + *
  • + * While the rendering thread is paused, rendering commands will continued to be queued + * until the buffer limit is reached. When the limit is reached, the program will abort. + *
*/ public void setPaused(boolean paused) { nSetPaused(getNativeObject(), paused); diff --git a/filament/backend/src/CommandBufferQueue.cpp b/filament/backend/src/CommandBufferQueue.cpp index 9de07ed0e27..b721ce0c50f 100644 --- a/filament/backend/src/CommandBufferQueue.cpp +++ b/filament/backend/src/CommandBufferQueue.cpp @@ -58,6 +58,7 @@ void CommandBufferQueue::requestExit() { } void CommandBufferQueue::setPaused(bool paused) { + std::lock_guard const lock(mLock); if (paused) { mPaused = true; } else { @@ -125,6 +126,9 @@ void CommandBufferQueue::flush() noexcept { #endif SYSTRACE_NAME("waiting: CircularBuffer::flush()"); + ASSERT_POSTCONDITION(!mPaused, + "CommandStream is full, but since the rendering thread is paused, " + "the buffer cannot flush and we will deadlock. Instead, abort."); mCondition.wait(lock, [this, requiredSize]() -> bool { // TODO: on macOS, we need to call pumpEvents from time to time return mFreeSpace >= requiredSize; diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index a702b3de263..784c9ccfc6b 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -403,9 +403,10 @@ class UTILS_PUBLIC Engine { Builder& featureLevel(FeatureLevel featureLevel) noexcept; /** + * Warning: This is an experimental API. See Engine::setPaused(bool) for caveats. + * * @param paused Whether to start the rendering thread paused. * @return A reference to this Builder for chaining calls. - * @warning Experimental. */ Builder& paused(bool paused) noexcept; @@ -836,7 +837,16 @@ class UTILS_PUBLIC Engine { /** * Pause or resume rendering thread. - * @warning Experimental. + * + *

Warning: This is an experimental API. In particular, note the following caveats. + * + *

  • + * Buffer callbacks will never be called as long as the rendering thread is paused. + * Do not rely on a buffer callback to unpause the thread. + *
  • + * While the rendering thread is paused, rendering commands will continued to be queued + * until the buffer limit is reached. When the limit is reached, the program will abort. + *
*/ void setPaused(bool paused); From aad45d9119338e8188d5fd1206d34f4a9d07516b Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Thu, 21 Mar 2024 12:59:20 -0700 Subject: [PATCH 11/28] Fix typo --- .../src/main/java/com/google/android/filament/Engine.java | 4 ++-- filament/include/filament/Engine.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 1e430b51386..29aa5fb1c58 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -1212,8 +1212,8 @@ public void flush() { * Buffer callbacks will never be called as long as the rendering thread is paused. * Do not rely on a buffer callback to unpause the thread. *
  • - * While the rendering thread is paused, rendering commands will continued to be queued - * until the buffer limit is reached. When the limit is reached, the program will abort. + * While the rendering thread is paused, rendering commands will continue to be queued until the + * buffer limit is reached. When the limit is reached, the program will abort. *
  • */ public void setPaused(boolean paused) { diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 784c9ccfc6b..8c2936ccc35 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -844,8 +844,8 @@ class UTILS_PUBLIC Engine { * Buffer callbacks will never be called as long as the rendering thread is paused. * Do not rely on a buffer callback to unpause the thread. *
  • - * While the rendering thread is paused, rendering commands will continued to be queued - * until the buffer limit is reached. When the limit is reached, the program will abort. + * While the rendering thread is paused, rendering commands will continue to be queued until the + * buffer limit is reached. When the limit is reached, the program will abort. *
  • */ void setPaused(bool paused); From 4d774820d968c88cde9f1a4fdbd34785521d6d7f Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 21 Mar 2024 12:31:38 -0700 Subject: [PATCH 12/28] gl: Don't reference swapchain obj on Web Fixes #7693 --- filament/backend/src/opengl/OpenGLDriver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 9113f094f9c..23b83ebe170 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -1462,10 +1462,12 @@ void OpenGLDriver::createSwapChainR(Handle sch, void* nativeWindow, GLSwapChain* sc = handle_cast(sch); sc->swapChain = mPlatform.createSwapChain(nativeWindow, flags); +#if !defined(__EMSCRIPTEN__) // note: in practice this should never happen on Android ASSERT_POSTCONDITION(sc->swapChain, "createSwapChain(%p, 0x%lx) failed. See logs for details.", nativeWindow, flags); +#endif // See if we need the emulated rec709 output conversion if (UTILS_UNLIKELY(mContext.isES2())) { @@ -1481,10 +1483,12 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle sch, GLSwapChain* sc = handle_cast(sch); sc->swapChain = mPlatform.createSwapChain(width, height, flags); +#if !defined(__EMSCRIPTEN__) // note: in practice this should never happen on Android ASSERT_POSTCONDITION(sc->swapChain, "createSwapChainHeadless(%u, %u, 0x%lx) failed. See logs for details.", width, height, flags); +#endif // See if we need the emulated rec709 output conversion if (UTILS_UNLIKELY(mContext.isES2())) { From 3644e7f80827f1cd2caef4a21e410a2243eb6e84 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 20 Mar 2024 15:57:22 -0700 Subject: [PATCH 13/28] Fixes IBL prefilter has floor and ceiling flipped FIXES=[330603077] --- libs/iblprefilter/src/materials/equirectToCube.mat | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/iblprefilter/src/materials/equirectToCube.mat b/libs/iblprefilter/src/materials/equirectToCube.mat index eb147998adf..36b20e21e23 100644 --- a/libs/iblprefilter/src/materials/equirectToCube.mat +++ b/libs/iblprefilter/src/materials/equirectToCube.mat @@ -56,7 +56,7 @@ highp vec2 toEquirect(const highp vec3 s) { highp float xf = atan(s.x, s.z) * (1.0 / PI); // range [-1.0, 1.0] highp float yf = asin(s.y) * (2.0 / PI); // range [-1.0, 1.0] xf = (xf + 1.0) * 0.5; // range [0, 1.0] - yf = (yf + 1.0) * 0.5; // range [0, 1.0] + yf = (1.0 - yf) * 0.5; // range [0, 1.0] return vec2(xf, yf); } @@ -67,7 +67,9 @@ mediump vec3 sampleEquirect(mediump sampler2D equirect, const highp vec3 r) { void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; // interpolated at pixel's center - highp vec2 p = uv * 2.0 - 1.0; + highp vec2 p = vec2( + uv.x * 2.0 - 1.0, + 1.0 - uv.y * 2.0); float side = materialParams.side; float mirror = materialParams.mirror; From 26258a471836417f362064f7d699a1a23c893267 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Fri, 22 Mar 2024 10:09:08 -0700 Subject: [PATCH 14/28] Facilitate four views for multiview (#7694) This change allows the combine function for multiview to be able to combine more than two views side-by-side. --- filament/src/PostProcessManager.cpp | 30 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 461acba3a20..3f21ce872d4 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -3292,8 +3292,9 @@ FrameGraphId PostProcessManager::debugCombineArrayTexture(Fra SamplerMagFilter filterMag, SamplerMinFilter filterMin) noexcept { - assert_invariant(fg.getDescriptor(input).depth > 1); - assert_invariant(fg.getDescriptor(input).type == SamplerType::SAMPLER_2D_ARRAY); + auto& inputTextureDesc = fg.getDescriptor(input); + assert_invariant(inputTextureDesc.depth > 1); + assert_invariant(inputTextureDesc.type == SamplerType::SAMPLER_2D_ARRAY); // TODO: add support for sub-resources assert_invariant(fg.getSubResourceDescriptor(input).layer == 0); @@ -3329,14 +3330,12 @@ FrameGraphId PostProcessManager::debugCombineArrayTexture(Fra .filterMag = filterMag, .filterMin = filterMin }); - mi->setParameter("layerIndex", 0); mi->setParameter("viewport", float4{ float(vp.left) / inputDesc.width, float(vp.bottom) / inputDesc.height, float(vp.width) / inputDesc.width, float(vp.height) / inputDesc.height }); - mi->commit(driver); mi->use(driver); auto pipeline = material.getPipelineState(mEngine); @@ -3347,18 +3346,19 @@ FrameGraphId PostProcessManager::debugCombineArrayTexture(Fra pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA; } - // Blit the scene rendered to the first layer to the left half of the screen. - out.params.viewport.width /= 2; - render(out, pipeline, driver); + // The width of each view takes up 1/depth of the screen width. + out.params.viewport.width /= inputTextureDesc.depth; - // Blit the scene rendered to the second layer to the right half of the screen. - // Don't clear or discard the target to keep the previously rendered left half image. - mi->setParameter("layerIndex", 1); - mi->commit(driver); - out.params.flags.clear = filament::backend::TargetBufferFlags::NONE; - out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE; - out.params.viewport.left += out.params.viewport.width; - render(out, pipeline, driver); + // Render all layers of the texture to the screen side-by-side. + for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) { + mi->setParameter("layerIndex", i); + mi->commit(driver); + render(out, pipeline, driver); + // From the second draw, don't clear the targetbuffer. + out.params.flags.clear = filament::backend::TargetBufferFlags::NONE; + out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE; + out.params.viewport.left += out.params.viewport.width; + } }); return ppQuadBlit->output; From 8398175d9cf9f678f065f521839888b4cfcdb2c5 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 21 Mar 2024 16:46:24 -0700 Subject: [PATCH 15/28] Add an option to disable use-after-free checks in the backend BUGS=330403836 --- android/filament-android/src/main/cpp/Engine.cpp | 4 +++- .../java/com/google/android/filament/Engine.java | 11 +++++++++-- filament/backend/include/backend/Platform.h | 5 +++++ .../include/private/backend/HandleAllocator.h | 11 ++++++----- filament/backend/src/HandleAllocator.cpp | 12 ++++++++---- filament/backend/src/metal/MetalDriver.mm | 4 +++- filament/backend/src/opengl/OpenGLDriver.cpp | 4 +++- filament/backend/src/vulkan/VulkanDriver.cpp | 2 +- .../backend/src/vulkan/VulkanResourceAllocator.h | 4 ++-- filament/include/filament/Engine.h | 5 +++++ filament/src/details/Engine.cpp | 6 ++++-- 11 files changed, 49 insertions(+), 19 deletions(-) diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index c9fa78dccd8..2677e2f8fa2 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -494,7 +494,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu jlong jobSystemThreadCount, jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile, jint stereoscopicType, jlong stereoscopicEyeCount, - jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) { + jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge, + jboolean disableHandleUseAfterFreeCheck) { Engine::Builder* builder = (Engine::Builder*) nativeBuilder; Engine::Config config = { .commandBufferSizeMB = (uint32_t) commandBufferSizeMB, @@ -509,6 +510,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu .stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount, .resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB, .resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge, + .disableHandleUseAfterFreeCheck = (bool) disableHandleUseAfterFreeCheck, }; builder->config(&config); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 29aa5fb1c58..97301df9806 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -224,7 +224,8 @@ public Builder config(Config config) { config.jobSystemThreadCount, config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile, config.stereoscopicType.ordinal(), config.stereoscopicEyeCount, - config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge); + config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge, + config.disableHandleUseAfterFreeCheck); return this; } @@ -422,6 +423,11 @@ public static class Config { * This value determines for how many frames are texture entries kept in the cache. */ public long resourceAllocatorCacheMaxAge = 2; + + /* + * Disable backend handles use-after-free checks. + */ + public boolean disableHandleUseAfterFreeCheck = false; } private Engine(long nativeEngine, Config config) { @@ -1314,7 +1320,8 @@ private static native void nSetBuilderConfig(long nativeBuilder, long commandBuf long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount, long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile, int stereoscopicType, long stereoscopicEyeCount, - long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge); + long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge, + boolean disableHandleUseAfterFreeCheck); private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal); private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext); private static native void nSetBuilderPaused(long nativeBuilder, boolean paused); diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index 1d85568a883..f1449795443 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -59,6 +59,11 @@ class UTILS_PUBLIC Platform { * Currently only honored by the GL and Metal backends. */ bool disableParallelShaderCompile = false; + + /** + * Disable backend handles use-after-free checks. + */ + bool disableHandleUseAfterFreeCheck = false; }; Platform() noexcept; diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 53bab210777..0337b41b0d1 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -49,7 +49,7 @@ namespace filament::backend { template class HandleAllocator { public: - HandleAllocator(const char* name, size_t size) noexcept; + HandleAllocator(const char* name, size_t size, bool disableUseAfterFreeCheck) noexcept; HandleAllocator(HandleAllocator const& rhs) = delete; HandleAllocator& operator=(HandleAllocator const& rhs) = delete; ~HandleAllocator(); @@ -172,7 +172,7 @@ class HandleAllocator { uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT; auto const pNode = static_cast(p); uint8_t const expectedAge = pNode[-1].age; - ASSERT_POSTCONDITION(expectedAge == age, + ASSERT_POSTCONDITION(!mUseAfterFreeCheckDisabled && expectedAge == age, "use-after-free of Handle with id=%d", handle.getId()); } @@ -187,7 +187,6 @@ class HandleAllocator { return handle_cast(const_cast&>(handle)); } - private: template @@ -210,8 +209,9 @@ class HandleAllocator { Pool mPool1; Pool mPool2; UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea; + bool mUseAfterFreeCheckDisabled; public: - explicit Allocator(const utils::AreaPolicy::HeapArea& area); + explicit Allocator(const utils::AreaPolicy::HeapArea& area, bool disableUseAfterFreeCheck); static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; } @@ -237,7 +237,7 @@ class HandleAllocator { // check for double-free Node* const pNode = static_cast(p); uint8_t& expectedAge = pNode[-1].age; - ASSERT_POSTCONDITION(expectedAge == age, + ASSERT_POSTCONDITION(!mUseAfterFreeCheckDisabled && expectedAge == age, "double-free of Handle of size %d at %p", size, p); expectedAge = (expectedAge + 1) & 0xF; // fixme @@ -348,6 +348,7 @@ class HandleAllocator { mutable utils::Mutex mLock; tsl::robin_map mOverflowMap; HandleBase::HandleId mId = 0; + bool mUseAfterFreeCheckDisabled = false; }; } // namespace filament::backend diff --git a/filament/backend/src/HandleAllocator.cpp b/filament/backend/src/HandleAllocator.cpp index bf8e779614c..fb58cb1b588 100644 --- a/filament/backend/src/HandleAllocator.cpp +++ b/filament/backend/src/HandleAllocator.cpp @@ -39,8 +39,10 @@ using namespace utils; template UTILS_NOINLINE -HandleAllocator::Allocator::Allocator(AreaPolicy::HeapArea const& area) - : mArea(area) { +HandleAllocator::Allocator::Allocator(AreaPolicy::HeapArea const& area, + bool disableUseAfterFreeCheck) + : mArea(area), + mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) { // The largest handle this allocator can generate currently depends on the architecture's // min alignment, typically 8 or 16 bytes. @@ -74,8 +76,10 @@ HandleAllocator::Allocator::Allocator(AreaPolicy::HeapArea const& ar // ------------------------------------------------------------------------------------------------ template -HandleAllocator::HandleAllocator(const char* name, size_t size) noexcept - : mHandleArena(name, size) { +HandleAllocator::HandleAllocator(const char* name, size_t size, + bool disableUseAfterFreeCheck) noexcept + : mHandleArena(name, size, disableUseAfterFreeCheck), + mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck) { } template diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 518df89f525..1a87b317717 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -98,7 +98,9 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept : mPlatform(*platform), mContext(new MetalContext(driverConfig.textureUseAfterFreePoolSize)), - mHandleAllocator("Handles", driverConfig.handleArenaSize) { + mHandleAllocator("Handles", + driverConfig.handleArenaSize, + driverConfig.disableHandleUseAfterFreeCheck) { mContext->driver = this; mContext->device = mPlatform.createDevice(); diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 23b83ebe170..196d1bd2e66 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -205,7 +205,9 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi : mPlatform(*platform), mContext(mPlatform), mShaderCompilerService(*this), - mHandleAllocator("Handles", driverConfig.handleArenaSize), + mHandleAllocator("Handles", + driverConfig.handleArenaSize, + driverConfig.disableHandleUseAfterFreeCheck), mSamplerMap(32), mDriverConfig(driverConfig) { diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index b140f43369d..ce950fbf571 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -209,7 +209,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(), mPlatform->getDevice())), mContext(context), - mResourceAllocator(driverConfig.handleArenaSize), + mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck), mResourceManager(&mResourceAllocator), mThreadSafeResourceManager(&mResourceAllocator), mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), diff --git a/filament/backend/src/vulkan/VulkanResourceAllocator.h b/filament/backend/src/vulkan/VulkanResourceAllocator.h index ac71f0ece09..8563946d484 100644 --- a/filament/backend/src/vulkan/VulkanResourceAllocator.h +++ b/filament/backend/src/vulkan/VulkanResourceAllocator.h @@ -51,8 +51,8 @@ namespace filament::backend { class VulkanResourceAllocator { public: - VulkanResourceAllocator(size_t arenaSize) - : mHandleAllocatorImpl("Handles", arenaSize) + VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck) + : mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck) #if DEBUG_RESOURCE_LEAKS , mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) { std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 8c2936ccc35..b741d3abd93 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -335,6 +335,11 @@ class UTILS_PUBLIC Engine { * This value determines for how many frames are texture entries kept in the cache. */ uint32_t resourceAllocatorCacheMaxAge = 2; + + /* + * Disable backend handles use-after-free checks. + */ + bool disableHandleUseAfterFreeCheck = false; }; diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index c0d3f802fba..dbd49a8b712 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -101,7 +101,8 @@ Engine* FEngine::create(Engine::Builder const& builder) { DriverConfig const driverConfig{ .handleArenaSize = instance->getRequestedDriverHandleArenaSize(), .textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize, - .disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile + .disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile, + .disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck }; instance->mDriver = platform->createDriver(sharedContext, driverConfig); @@ -665,7 +666,8 @@ int FEngine::loop() { DriverConfig const driverConfig { .handleArenaSize = getRequestedDriverHandleArenaSize(), .textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize, - .disableParallelShaderCompile = mConfig.disableParallelShaderCompile + .disableParallelShaderCompile = mConfig.disableParallelShaderCompile, + .disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck }; mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig); From d2ce714e73bab8d5915a79b0038a241c933616bc Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 22 Mar 2024 11:02:08 -0700 Subject: [PATCH 16/28] fix use-after-free disable option --- .../include/private/backend/HandleAllocator.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 0337b41b0d1..578ce5d6f1a 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -169,11 +169,13 @@ class HandleAllocator { if (isPoolHandle(handle.getId())) { // check for use after free - uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT; - auto const pNode = static_cast(p); - uint8_t const expectedAge = pNode[-1].age; - ASSERT_POSTCONDITION(!mUseAfterFreeCheckDisabled && expectedAge == age, - "use-after-free of Handle with id=%d", handle.getId()); + if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) { + uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT; + auto const pNode = static_cast(p); + uint8_t const expectedAge = pNode[-1].age; + ASSERT_POSTCONDITION(expectedAge == age, + "use-after-free of Handle with id=%d", handle.getId()); + } } return static_cast(p); @@ -237,8 +239,10 @@ class HandleAllocator { // check for double-free Node* const pNode = static_cast(p); uint8_t& expectedAge = pNode[-1].age; - ASSERT_POSTCONDITION(!mUseAfterFreeCheckDisabled && expectedAge == age, - "double-free of Handle of size %d at %p", size, p); + if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) { + ASSERT_POSTCONDITION(expectedAge == age, + "double-free of Handle of size %d at %p", size, p); + } expectedAge = (expectedAge + 1) & 0xF; // fixme if (size <= mPool0.getSize()) { mPool0.free(p); return; } From 9aaaad9271df04a755c7ee5c27bed050bb0131bb Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Fri, 22 Mar 2024 12:03:06 -0700 Subject: [PATCH 17/28] Improve Skybox eyeDirection precision (#7685) --- filament/src/materials/skybox.mat | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/filament/src/materials/skybox.mat b/filament/src/materials/skybox.mat index d692fc238cf..c0a1181dbfa 100644 --- a/filament/src/materials/skybox.mat +++ b/filament/src/materials/skybox.mat @@ -58,6 +58,15 @@ fragment { vertex { void materialVertex(inout MaterialVertexInputs material) { - material.eyeDirection.xyz = material.worldPosition.xyz; + // This code is taken from computeWorldPosition and assumes the vertex domain is 'device'. + vec4 p = getPosition(); + // GL convention to inverted DX convention + p.z = p.z * -0.5 + 0.5; + vec4 worldPosition = getWorldFromClipMatrix() * p; + // Getting the true world position would require dividing by w, but since this is a skybox + // at inifinity, this results in very large numbers for material.eyeDirection. + // Since the eyeDirection is only used as a direction vector in the fragment shader, we can + // skip that step to improve precision. + material.eyeDirection.xyz = worldPosition.xyz; } } From 317c1bb7ea2c1aacb2ba156136e87d6b4a97c47d Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Fri, 22 Mar 2024 12:47:56 -0700 Subject: [PATCH 18/28] Metal: track buffer allocations (#7556) --- filament/backend/include/backend/Platform.h | 31 +++++++++++ filament/backend/src/Platform.cpp | 14 +++++ filament/backend/src/metal/MetalBuffer.h | 54 ++++++++++++++++--- filament/backend/src/metal/MetalBuffer.mm | 11 ++-- filament/backend/src/metal/MetalBufferPool.h | 4 +- filament/backend/src/metal/MetalBufferPool.mm | 4 +- filament/backend/src/metal/MetalDriver.mm | 5 ++ filament/backend/src/metal/MetalHandles.mm | 5 +- libs/filamentapp/src/FilamentApp.cpp | 1 + libs/utils/include/utils/ostream.h | 1 + libs/utils/include/utils/sstream.h | 1 + libs/utils/src/ostream.cpp | 4 ++ libs/utils/src/sstream.cpp | 4 ++ libs/utils/test/test_sstream.cpp | 13 +++++ 14 files changed, 136 insertions(+), 16 deletions(-) diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index f1449795443..af99d4e2be5 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -193,9 +193,40 @@ 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; + + /** + * 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. + * + * @param debugUpdateStat an Invocable that updates debug statistics + */ + void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept; + + /** + * @return true if debugUpdateStat is valid. + */ + bool hasDebugUpdateStatFunc() const noexcept; + + /** + * 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 value the updated value of key + */ + void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value); + private: InsertBlobFunc mInsertBlob; RetrieveBlobFunc mRetrieveBlob; + DebugUpdateStatFunc mDebugUpdateStat; }; } // namespace filament diff --git a/filament/backend/src/Platform.cpp b/filament/backend/src/Platform.cpp index db2fd0eafdf..77c85129417 100644 --- a/filament/backend/src/Platform.cpp +++ b/filament/backend/src/Platform.cpp @@ -53,4 +53,18 @@ size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size return 0; } +void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept { + mDebugUpdateStat = std::move(debugUpdateStat); +} + +bool Platform::hasDebugUpdateStatFunc() const noexcept { + return bool(mDebugUpdateStat); +} + +void Platform::debugUpdateStat(const char* key, uint64_t value) { + if (mDebugUpdateStat) { + mDebugUpdateStat(key, value); + } +} + } // namespace filament::backend diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h index 4baccffc2ce..579975d0d6c 100644 --- a/filament/backend/src/metal/MetalBuffer.h +++ b/filament/backend/src/metal/MetalBuffer.h @@ -18,7 +18,6 @@ #define TNT_FILAMENT_DRIVER_METALBUFFER_H #include "MetalContext.h" -#include "MetalBufferPool.h" #include @@ -28,9 +27,50 @@ #include #include +#include namespace filament::backend { +class TrackedMetalBuffer { +public: + TrackedMetalBuffer() noexcept : mBuffer(nil) {} + TrackedMetalBuffer(id buffer) noexcept : mBuffer(buffer) { + if (buffer) { + aliveBuffers++; + } + } + ~TrackedMetalBuffer() { + if (mBuffer) { + aliveBuffers--; + } + } + + TrackedMetalBuffer(TrackedMetalBuffer&&) = delete; + TrackedMetalBuffer(TrackedMetalBuffer const&) = delete; + TrackedMetalBuffer& operator=(TrackedMetalBuffer const&) = delete; + + TrackedMetalBuffer& operator=(TrackedMetalBuffer&& rhs) noexcept { + swap(rhs); + return *this; + } + + id get() const noexcept { return mBuffer; } + operator bool() const noexcept { return bool(mBuffer); } + + static uint64_t getAliveBuffers() { return aliveBuffers; } + +private: + void swap(TrackedMetalBuffer& other) noexcept { + id temp = mBuffer; + mBuffer = other.mBuffer; + other.mBuffer = temp; + } + + id mBuffer; + + static std::atomic aliveBuffers; +}; + class MetalBuffer { public: @@ -82,7 +122,7 @@ class MetalBuffer { private: - id mBuffer = nil; + TrackedMetalBuffer mBuffer; size_t mBufferSize = 0; void* mCpuBuffer = nullptr; MetalContext& mContext; @@ -151,7 +191,7 @@ class MetalRingBuffer { // finishes executing. mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions]; assert_invariant(mAuxBuffer); - return {mAuxBuffer, 0}; + return {mAuxBuffer.get(), 0}; } mCurrentSlot = (mCurrentSlot + 1) % mSlotCount; mOccupiedSlots->fetch_add(1, std::memory_order_relaxed); @@ -180,9 +220,9 @@ class MetalRingBuffer { */ std::pair, NSUInteger> getCurrentAllocation() const { if (UTILS_UNLIKELY(mAuxBuffer)) { - return { mAuxBuffer, 0 }; + return { mAuxBuffer.get(), 0 }; } - return { mBuffer, mCurrentSlot * mSlotSizeBytes }; + return { mBuffer.get(), mCurrentSlot * mSlotSizeBytes }; } bool canAccomodateLayout(MTLSizeAndAlign layout) const { @@ -191,8 +231,8 @@ class MetalRingBuffer { private: id mDevice; - id mBuffer; - id mAuxBuffer; + TrackedMetalBuffer mBuffer; + TrackedMetalBuffer mAuxBuffer; MTLResourceOptions mBufferOptions; diff --git a/filament/backend/src/metal/MetalBuffer.mm b/filament/backend/src/metal/MetalBuffer.mm index 4b04e4d5c84..af46027e20d 100644 --- a/filament/backend/src/metal/MetalBuffer.mm +++ b/filament/backend/src/metal/MetalBuffer.mm @@ -15,12 +15,15 @@ */ #include "MetalBuffer.h" +#include "MetalBufferPool.h" #include "MetalContext.h" namespace filament { namespace backend { +std::atomic TrackedMetalBuffer::aliveBuffers = 0; + MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage, size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) { // If the buffer is less than 4K in size and is updated frequently, we don't use an explicit @@ -61,7 +64,7 @@ // Acquire a staging buffer to hold the contents of this update. MetalBufferPool* bufferPool = mContext.bufferPool; const MetalBufferPoolEntry* const staging = bufferPool->acquireBuffer(size); - memcpy(staging->buffer.contents, src, size); + memcpy(staging->buffer.get().contents, src, size); // The blit below requires that byteOffset be a multiple of 4. ASSERT_PRECONDITION(!(byteOffset & 0x3u), "byteOffset must be a multiple of 4"); @@ -70,9 +73,9 @@ id cmdBuffer = getPendingCommandBuffer(&mContext); id blitEncoder = [cmdBuffer blitCommandEncoder]; blitEncoder.label = @"Buffer upload blit"; - [blitEncoder copyFromBuffer:staging->buffer + [blitEncoder copyFromBuffer:staging->buffer.get() sourceOffset:0 - toBuffer:mBuffer + toBuffer:mBuffer.get() destinationOffset:byteOffset size:size]; [blitEncoder endEncoding]; @@ -93,7 +96,7 @@ return nil; } assert_invariant(mBuffer); - return mBuffer; + return mBuffer.get(); } void MetalBuffer::bindBuffers(id cmdBuffer, id encoder, diff --git a/filament/backend/src/metal/MetalBufferPool.h b/filament/backend/src/metal/MetalBufferPool.h index 68e056ed404..03688ab3c43 100644 --- a/filament/backend/src/metal/MetalBufferPool.h +++ b/filament/backend/src/metal/MetalBufferPool.h @@ -19,6 +19,8 @@ #include +#include "MetalBuffer.h" + #include #include #include @@ -30,7 +32,7 @@ struct MetalContext; // Immutable POD representing a shared CPU-GPU buffer. struct MetalBufferPoolEntry { - id buffer; + TrackedMetalBuffer buffer; size_t capacity; mutable uint64_t lastAccessed; mutable uint32_t referenceCount; diff --git a/filament/backend/src/metal/MetalBufferPool.mm b/filament/backend/src/metal/MetalBufferPool.mm index 29915d4aacb..3b75c8e85d4 100644 --- a/filament/backend/src/metal/MetalBufferPool.mm +++ b/filament/backend/src/metal/MetalBufferPool.mm @@ -45,12 +45,12 @@ id buffer = [mContext.device newBufferWithLength:numBytes options:MTLResourceStorageModeShared]; ASSERT_POSTCONDITION(buffer, "Could not allocate Metal staging buffer of size %zu.", numBytes); - MetalBufferPoolEntry* stage = new MetalBufferPoolEntry({ + MetalBufferPoolEntry* stage = new MetalBufferPoolEntry { .buffer = buffer, .capacity = numBytes, .lastAccessed = mCurrentFrame, .referenceCount = 1 - }); + }; mUsedStages.insert(stage); return stage; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 1a87b317717..f0e23ccb033 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -20,6 +20,7 @@ #include "metal/MetalDriver.h" #include "MetalBlitter.h" +#include "MetalBufferPool.h" #include "MetalContext.h" #include "MetalDriverFactory.h" #include "MetalEnums.h" @@ -36,6 +37,7 @@ #include #include +#include #include @@ -214,6 +216,9 @@ #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId); #endif + if (mPlatform.hasDebugUpdateStatFunc()) { + mPlatform.debugUpdateStat("filament.metal.alive_buffers", TrackedMetalBuffer::getAliveBuffers()); + } } void MetalDriver::setFrameScheduledCallback(Handle sch, diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 18d83118253..0d9976211da 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -19,6 +19,7 @@ #include "MetalBlitter.h" #include "MetalEnums.h" #include "MetalUtils.h" +#include "MetalBufferPool.h" #include @@ -770,13 +771,13 @@ void presentDrawable(bool presentFrame, void* user) { PixelBufferDescriptor const& data, const PixelBufferShape& shape) { const size_t stagingBufferSize = shape.totalBytes; auto entry = context.bufferPool->acquireBuffer(stagingBufferSize); - memcpy(entry->buffer.contents, + memcpy(entry->buffer.get().contents, static_cast(data.buffer) + shape.sourceOffset, stagingBufferSize); id blitCommandBuffer = getPendingCommandBuffer(&context); id blitCommandEncoder = [blitCommandBuffer blitCommandEncoder]; blitCommandEncoder.label = @"Texture upload buffer blit"; - [blitCommandEncoder copyFromBuffer:entry->buffer + [blitCommandEncoder copyFromBuffer:entry->buffer.get() sourceOffset:0 sourceBytesPerRow:shape.bytesPerRow sourceBytesPerImage:shape.bytesPerSlice diff --git a/libs/filamentapp/src/FilamentApp.cpp b/libs/filamentapp/src/FilamentApp.cpp index 25f7126f834..82eba7a2316 100644 --- a/libs/filamentapp/src/FilamentApp.cpp +++ b/libs/filamentapp/src/FilamentApp.cpp @@ -674,6 +674,7 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, mSwapChain = mFilamentApp->mEngine->createSwapChain( nativeSwapChain, filament::SwapChain::CONFIG_HAS_STENCIL_BUFFER); } + mRenderer = mFilamentApp->mEngine->createRenderer(); // create cameras diff --git a/libs/utils/include/utils/ostream.h b/libs/utils/include/utils/ostream.h index 623cf9c08e2..cde8e75bd27 100644 --- a/libs/utils/include/utils/ostream.h +++ b/libs/utils/include/utils/ostream.h @@ -92,6 +92,7 @@ class UTILS_PUBLIC ostream : protected utils::PrivateImplementation { std::pair grow(size_t s) noexcept; void advance(ssize_t n) noexcept; void reset() noexcept; + size_t length() const noexcept; private: void reserve(size_t newSize) noexcept; diff --git a/libs/utils/include/utils/sstream.h b/libs/utils/include/utils/sstream.h index 8b0c4b4a2a6..605b678e6d3 100644 --- a/libs/utils/include/utils/sstream.h +++ b/libs/utils/include/utils/sstream.h @@ -25,6 +25,7 @@ class sstream : public ostream { public: ostream& flush() noexcept override; const char* c_str() const noexcept; + size_t length() const noexcept; }; } // namespace utils::io diff --git a/libs/utils/src/ostream.cpp b/libs/utils/src/ostream.cpp index 579dc9e36e9..35ffab84347 100644 --- a/libs/utils/src/ostream.cpp +++ b/libs/utils/src/ostream.cpp @@ -267,6 +267,10 @@ void ostream::Buffer::reset() noexcept { size = capacity; } +size_t ostream::Buffer::length() const noexcept { + return curr - buffer; +} + std::pair ostream::Buffer::grow(size_t s) noexcept { if (UTILS_UNLIKELY(size < s)) { size_t const used = curr - buffer; diff --git a/libs/utils/src/sstream.cpp b/libs/utils/src/sstream.cpp index 1c02fd862bb..a3cc80a3a75 100644 --- a/libs/utils/src/sstream.cpp +++ b/libs/utils/src/sstream.cpp @@ -28,4 +28,8 @@ const char* sstream::c_str() const noexcept { return buffer ? buffer : ""; } +size_t sstream::length() const noexcept { + return getBuffer().length(); +} + } // namespace utils::io diff --git a/libs/utils/test/test_sstream.cpp b/libs/utils/test/test_sstream.cpp index fbac6242790..9a832faf2ed 100644 --- a/libs/utils/test/test_sstream.cpp +++ b/libs/utils/test/test_sstream.cpp @@ -144,6 +144,7 @@ TEST(sstream, LargeBuffer) { } EXPECT_EQ(1024 * 1024 * 16, strlen(ss.c_str())); + EXPECT_EQ(1024 * 1024 * 16, ss.length()); } TEST(sstream, LargeString) { @@ -158,6 +159,7 @@ TEST(sstream, LargeString) { ss << filler; EXPECT_EQ(size, strlen(ss.c_str())); + EXPECT_EQ(size, ss.length()); EXPECT_STREQ(filler, ss.c_str()); free(filler); @@ -182,7 +184,18 @@ TEST(sstream, SeveralStrings) { ss << fillerB; EXPECT_EQ(sizeA + sizeB, strlen(ss.c_str())); + EXPECT_EQ(sizeA + sizeB, ss.length()); free(fillerA); free(fillerB); } + +TEST(sstream, length) { + sstream ss; + + EXPECT_EQ(0, ss.length()); + ss << "Hello, world\n"; + EXPECT_EQ(13, ss.length()); + ss << "Foo bar\n"; + EXPECT_EQ(13 + 8, ss.length()); +} From 0ead96b606d8802635b4c2de24db70fbb46f2248 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Fri, 22 Mar 2024 14:45:58 -0700 Subject: [PATCH 19/28] Replace value of num_views with engine's eye count (#7696) For OpenGL multiview, it honors the qualifier `layout(num_views = X)` specified in shader files to determine the number of views for multiview. We cannot recompile materials everytime the value changes. So replace the value of num_views with the engine's eye count when shaders compile. --- .../src/opengl/ShaderCompilerService.cpp | 73 +++++++++++++++++-- .../src/opengl/ShaderCompilerService.h | 5 +- .../include/private/filament/EngineEnums.h | 2 +- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index 3c1f57f8329..23fc15a9668 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -514,8 +515,16 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, }; std::string specializationConstantString; + int32_t numViews = 2; for (auto const& sc : specializationConstants) { appendSpecConstantString(specializationConstantString, sc); + if (sc.id == 8) { + // This constant must match + // ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT + // which we can't use here because it's defined in EngineEnums.h. + // (we're breaking layering here, but it's for the good cause). + numViews = std::get(sc.value); + } } if (!specializationConstantString.empty()) { specializationConstantString += '\n'; @@ -544,17 +553,23 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, if (UTILS_LIKELY(!shadersSource[i].empty())) { Program::ShaderBlob& shader = shadersSource[i]; + char* shader_src = reinterpret_cast(shader.data()); + size_t shader_len = shader.size(); // remove GOOGLE_cpp_style_line_directive - std::string_view const source = process_GOOGLE_cpp_style_line_directive(context, - reinterpret_cast(shader.data()), shader.size()); + process_GOOGLE_cpp_style_line_directive(context, shader_src, shader_len); + + // replace the value of layout(num_views = X) for multiview extension + if (stage == ShaderStage::VERTEX) { + process_OVR_multiview2(context, numViews, shader_src, shader_len); + } // add support for ARB_shading_language_packing if needed auto const packingFunctions = process_ARB_shading_language_packing(context); // split shader source, so we can insert the specialization constants and the packing // functions - auto const [prolog, body] = splitShaderSource(source); + auto const [prolog, body] = splitShaderSource({ shader_src, shader_len }); const std::array sources = { prolog.data(), @@ -577,7 +592,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, #ifndef NDEBUG // for debugging we return the original shader source (without the modifications we // made here), otherwise the line numbers wouldn't match. - outShaderSourceCode[i] = { source.data(), source.length() }; + outShaderSourceCode[i] = { shader_src, shader_len }; #endif outShaders[i] = shaderId; @@ -586,15 +601,59 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, } // If usages of the Google-style line directive are present, remove them, as some -// drivers don't allow the quotation marks. This happens in-place. -std::string_view ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, +// drivers don't allow the quotation marks. This source modification happens in-place. +void ShaderCompilerService::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, char* source, size_t len) noexcept { if (!context.ext.GOOGLE_cpp_style_line_directive) { if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) { removeGoogleLineDirectives(source, len); // length is unaffected } } - return { source, len }; +} + +// Look up the `source` to replace the number of eyes for multiview with the given number. This is +// necessary for OpenGL because OpenGL relies on the number specified in shader files to determine +// the number of views, which is assumed as a single digit, for multiview. +// This source modification happens in-place. +void ShaderCompilerService::process_OVR_multiview2(OpenGLContext& context, + int32_t eyeCount, char* source, size_t len) noexcept { + // We don't use regular expression in favor of performance. + if (context.ext.OVR_multiview2) { + const std::string_view shader{ source, len }; + const std::string_view layout = "layout"; + const std::string_view num_views = "num_views"; + size_t found = 0; + while (true) { + found = shader.find(layout, found); + if (found == std::string_view::npos) { + break; + } + found = shader.find_first_not_of(' ', found + layout.size()); + if (found == std::string_view::npos || shader[found] != '(') { + continue; + } + found = shader.find_first_not_of(' ', found + 1); + if (found == std::string_view::npos) { + continue; + } + if (shader.compare(found, num_views.size(), num_views) != 0) { + continue; + } + found = shader.find_first_not_of(' ', found + num_views.size()); + if (found == std::string_view::npos || shader[found] != '=') { + continue; + } + found = shader.find_first_not_of(' ', found + 1); + if (found == std::string_view::npos) { + continue; + } + // We assume the value should be one-digit number. + assert_invariant(eyeCount < 10); + assert_invariant(!::isdigit(source[found + 1])); + source[found] = '0' + eyeCount; + break; + } + } } // Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 (appeared in 4.2) and diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index edd89aafdbb..51430a1e54c 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -137,7 +137,10 @@ class ShaderCompilerService { std::array& outShaders, std::array& outShaderSourceCode) noexcept; - static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, + static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context, + char* source, size_t len) noexcept; + + static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount, char* source, size_t len) noexcept; static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept; diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index b29c63868d5..30b2f0663c6 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -68,7 +68,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_POWER_VR_SHADER_WORKAROUNDS = 5, CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6, CONFIG_DEBUG_FROXEL_VISUALIZATION = 7, - CONFIG_STEREO_EYE_COUNT = 8, + CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp) }; // This value is limited by UBO size, ES3.0 only guarantees 16 KiB. From 2763931b47c86783d0f9c4e375c83d38b6f1ff02 Mon Sep 17 00:00:00 2001 From: Jacob Su Date: Sat, 23 Mar 2024 14:05:01 +0800 Subject: [PATCH 20/28] fix install android samples apk error. (#7230) --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index d9a778c7ba2..56562cc1c6c 100755 --- a/build.sh +++ b/build.sh @@ -516,7 +516,7 @@ function build_android { if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then for sample in ${ANDROID_SAMPLES}; do echo "Installing out/${sample}-debug.apk" - cp samples/${sample}/build/outputs/apk/debug/${sample}-debug-unsigned.apk \ + cp samples/${sample}/build/outputs/apk/debug/${sample}-debug.apk \ ../out/${sample}-debug.apk done fi From abb0cbc98ea4821396c442ea2c4e655f2d687785 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Mon, 25 Mar 2024 14:43:04 -0700 Subject: [PATCH 21/28] Add FILAMENT_ENABLE_MULTIVIEW option (#7707) This allows the engine to include multiview shader code for default materials. --- CMakeLists.txt | 7 +++++++ filament/CMakeLists.txt | 7 ++++++- filament/src/details/Engine.cpp | 4 ++++ filament/src/details/Skybox.cpp | 4 ++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b18c4a5b4ed..ed32949873a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,8 @@ option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF) option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON) +option(FILAMENT_ENABLE_MULTIVIEW "Enable multiview for Filament" OFF) + set(FILAMENT_NDK_VERSION "" CACHE STRING "Android NDK version or version prefix to be used when building for Android." ) @@ -541,6 +543,11 @@ if (NOT FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "instanced" AND NOT FILAMENT_SAMPL message(FATAL_ERROR "Invalid stereo type: \"${FILAMENT_SAMPLES_STEREO_TYPE}\" choose either \"instanced\" or \"multiview\" ") endif () +# Compiling samples for multiview implies enabling multiview feature as well. +if (FILAMENT_SAMPLES_STEREO_TYPE STREQUAL "multiview") + set(FILAMENT_ENABLE_MULTIVIEW ON) +endif () + # ================================================================================================== # Material compilation flags # ================================================================================================== diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 66b78307cc8..b978d2da1bd 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -290,6 +290,11 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0) add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0) endif() +# Whether to include MULTIVIEW materials. +if (FILAMENT_ENABLE_MULTIVIEW) + add_definitions(-DFILAMENT_ENABLE_MULTIVIEW) +endif() + # ================================================================================================== # Definitions # ================================================================================================== @@ -343,7 +348,7 @@ foreach (mat_src ${MATERIAL_SRCS}) endif () list(FIND MATERIAL_MULTIVIEW_SRCS ${mat_src} index) - if (${index} GREATER -1) + if (${index} GREATER -1 AND FILAMENT_ENABLE_MULTIVIEW) string(REGEX REPLACE "[.]filamat$" "_multiview.filamat" output_path_multiview ${output_path}) add_custom_command( OUTPUT ${output_path_multiview} diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index dbd49a8b712..5f7396fb3d5 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -356,8 +356,12 @@ void FEngine::init() { MATERIALS_DEFAULTMATERIAL_DATA, MATERIALS_DEFAULTMATERIAL_SIZE); break; case StereoscopicType::MULTIVIEW: +#ifdef FILAMENT_ENABLE_MULTIVIEW defaultMaterialBuilder.package( MATERIALS_DEFAULTMATERIAL_MULTIVIEW_DATA, MATERIALS_DEFAULTMATERIAL_MULTIVIEW_SIZE); +#else + assert_invariant(false); +#endif break; } mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast(this))); diff --git a/filament/src/details/Skybox.cpp b/filament/src/details/Skybox.cpp index 7fe6291bc8c..39aca8ef8ca 100644 --- a/filament/src/details/Skybox.cpp +++ b/filament/src/details/Skybox.cpp @@ -129,7 +129,11 @@ FMaterial const* FSkybox::createMaterial(FEngine& engine) { builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE); break; case Engine::StereoscopicType::MULTIVIEW: +#ifdef FILAMENT_ENABLE_MULTIVIEW builder.package(MATERIALS_SKYBOX_MULTIVIEW_DATA, MATERIALS_SKYBOX_MULTIVIEW_SIZE); +#else + assert_invariant(false); +#endif break; } } From 2f359b73a434dd67b384f7784a938eca30e22294 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 26 Mar 2024 12:51:57 -0700 Subject: [PATCH 22/28] Add missing include to Platform.h (#7709) --- filament/backend/include/backend/Platform.h | 1 + 1 file changed, 1 insertion(+) diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index af99d4e2be5..03026dffe6e 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -23,6 +23,7 @@ #include #include +#include namespace filament::backend { From d70f2e1b81073f5ab6a712b4e83282feb0b350fe Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 26 Mar 2024 13:35:50 -0700 Subject: [PATCH 23/28] vk: delete instead of ref-count EmptyTexture (#7711) --- filament/backend/src/vulkan/VulkanDriver.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index ce950fbf571..d91eeb84b4d 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -249,9 +249,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, mStagePool); - // Use resource manager to ref-count placeholder resources. - mResourceManager.acquire(mEmptyTexture); - mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView()); } @@ -317,6 +314,7 @@ void VulkanDriver::terminate() { // are about to be destroyed. mCommands.terminate(); + delete mEmptyTexture; mResourceManager.clear(); mTimestamps.reset(); From 54df4524eb8d0c59afba0a43840b562b4a1de89b Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Tue, 26 Mar 2024 14:36:22 -0700 Subject: [PATCH 24/28] Improve multiview shader replacement (#7706) Replace the num_views for OpenGL multiviwe only when - The engine is initialized with multiview stereo - The variant for the material contains STE flag - The program is for surface - It's vertex shader (this is already in) --- filament/backend/include/backend/Program.h | 9 +++++++++ filament/backend/src/Program.cpp | 5 +++++ filament/backend/src/opengl/ShaderCompilerService.cpp | 5 ++++- filament/backend/src/opengl/ShaderCompilerService.h | 1 + filament/src/details/Material.cpp | 3 +++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/filament/backend/include/backend/Program.h b/filament/backend/include/backend/Program.h index 97deb6c586b..fe1c4a9b6e8 100644 --- a/filament/backend/include/backend/Program.h +++ b/filament/backend/include/backend/Program.h @@ -116,6 +116,8 @@ class Program { Program& cacheId(uint64_t cacheId) noexcept; + Program& multiview(bool multiview) noexcept; + ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } ShaderSource& getShadersSource() noexcept { return mShadersSource; } @@ -143,6 +145,8 @@ class Program { uint64_t getCacheId() const noexcept { return mCacheId; } + bool isMultiview() const noexcept { return mMultiview; } + CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } private: @@ -158,6 +162,11 @@ class Program { utils::FixedCapacityVector> mAttributes; std::array mBindingUniformInfo; CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; + // 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. + // This variable could be promoted as a more generic variable later if other similar needs occur. + bool mMultiview = false; }; } // namespace filament::backend diff --git a/filament/backend/src/Program.cpp b/filament/backend/src/Program.cpp index 1bae22d97d0..dc92e8c2a26 100644 --- a/filament/backend/src/Program.cpp +++ b/filament/backend/src/Program.cpp @@ -91,6 +91,11 @@ Program& Program::cacheId(uint64_t cacheId) noexcept { return *this; } +Program& Program::multiview(bool multiview) noexcept { + mMultiview = multiview; + return *this; +} + io::ostream& operator<<(io::ostream& out, const Program& builder) { out << "Program{"; builder.mLogger(out); diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index 23fc15a9668..5b9397ddd4b 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -265,6 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( compileShaders(gl, std::move(program.getShadersSource()), program.getSpecializationConstants(), + program.isMultiview(), shaders, token->shaderSourceCode); @@ -300,6 +301,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( compileShaders(gl, std::move(program.getShadersSource()), program.getSpecializationConstants(), + program.isMultiview(), token->gl.shaders, token->shaderSourceCode); @@ -502,6 +504,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept { void ShaderCompilerService::compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource, utils::FixedCapacityVector const& specializationConstants, + bool multiview, std::array& outShaders, UTILS_UNUSED_IN_RELEASE std::array& outShaderSourceCode) noexcept { @@ -560,7 +563,7 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context, process_GOOGLE_cpp_style_line_directive(context, shader_src, shader_len); // replace the value of layout(num_views = X) for multiview extension - if (stage == ShaderStage::VERTEX) { + if (multiview && stage == ShaderStage::VERTEX) { process_OVR_multiview2(context, numViews, shader_src, shader_len); } diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index 51430a1e54c..27255ea0002 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -134,6 +134,7 @@ class ShaderCompilerService { OpenGLContext& context, Program::ShaderSource shadersSource, utils::FixedCapacityVector const& specializationConstants, + bool multiview, std::array& outShaders, std::array& outShaderSourceCode) noexcept; diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 93745bf642b..66bf9ee67b2 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -641,6 +641,9 @@ void FMaterial::getSurfaceProgramSlow(Variant variant, Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) }; pb.priorityQueue(priorityQueue); + pb.multiview( + mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW && + Variant::isStereoVariant(variant)); createAndCacheProgram(std::move(pb), variant); } From 90d90094dc9eed59b7e75969b8128857217cea2c Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 26 Mar 2024 14:37:13 -0700 Subject: [PATCH 25/28] Bump material version to 51 in MaterialEnums.h --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 4c4d3da332b..ebe2ccce20e 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 50; +static constexpr size_t MATERIAL_VERSION = 51; /** * Supported shading models From a068143953d9f5feb7a79b5318898ccbced30080 Mon Sep 17 00:00:00 2001 From: Nimrod Gileadi <7824441+nimrod-gileadi@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:24:10 +0000 Subject: [PATCH 26/28] Add semicolons to code snippet in AssetLoader.h (#7717) --- libs/gltfio/include/gltfio/AssetLoader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/gltfio/include/gltfio/AssetLoader.h b/libs/gltfio/include/gltfio/AssetLoader.h index 080538aa336..f516166a800 100644 --- a/libs/gltfio/include/gltfio/AssetLoader.h +++ b/libs/gltfio/include/gltfio/AssetLoader.h @@ -97,8 +97,8 @@ struct AssetConfiguration { * * // Load buffers and textures from disk. * ResourceLoader resourceLoader({engine, ".", true}); - * resourceLoader.addTextureProvider("image/png", decoder) - * resourceLoader.addTextureProvider("image/jpeg", decoder) + * resourceLoader.addTextureProvider("image/png", decoder); + * resourceLoader.addTextureProvider("image/jpeg", decoder); * resourceLoader.loadResources(asset); * * // Free the glTF hierarchy as it is no longer needed. From f12e7f9fbf1e8b9c0fd39821d31675e072cbf401 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 28 Mar 2024 11:47:51 -0700 Subject: [PATCH 27/28] Release Filament 1.51.1 --- NEW_RELEASE_NOTES.md | 2 -- README.md | 4 ++-- RELEASE_NOTES.md | 4 ++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 77a7acc5f46..4a1a9c7fa7e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,5 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()` diff --git a/README.md b/README.md index 8a4adea8f24..a1dc91cdc6b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.51.0' + implementation 'com.google.android.filament:filament-android:1.51.1' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.51.0' +pod 'Filament', '~> 1.51.1' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ba94019fceb..d60e6d45843 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.51.2 + +- engine: Add experimental APIs `Engine::builder::paused()` and `Engine::setPaused()` + ## v1.51.1 diff --git a/android/gradle.properties b/android/gradle.properties index 535a3002648..685dd260796 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.51.0 +VERSION_NAME=1.51.1 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 513d6c19752..b51a82ab371 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.51.0" + spec.version = "1.51.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.0/filament-v1.51.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.1/filament-v1.51.1-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 2b2c4d8af55..7f24bbdf4a6 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.51.0", + "version": "1.51.1", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From d476c7fa1bac8ea359c96e0015571aa06b11d55f Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 28 Mar 2024 11:48:00 -0700 Subject: [PATCH 28/28] Bump version to 1.51.2 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a1dc91cdc6b..f2e6856085d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.51.1' + implementation 'com.google.android.filament:filament-android:1.51.2' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.51.1' +pod 'Filament', '~> 1.51.2' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 685dd260796..c677c8262ab 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.51.1 +VERSION_NAME=1.51.2 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index b51a82ab371..3ed06a44db5 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.51.1" + spec.version = "1.51.2" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.1/filament-v1.51.1-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.2/filament-v1.51.2-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 7f24bbdf4a6..62fb8be635a 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.51.1", + "version": "1.51.2", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",