From f11a04722bd92ff5ba1f85381c00b711ceb9800e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 7 Jun 2024 10:10:45 -0700 Subject: [PATCH 01/49] new descriptor set api --- .../src/main/cpp/RenderableManager.cpp | 15 +- .../android/filament/RenderableManager.java | 24 -- filament/CMakeLists.txt | 22 +- filament/backend/CMakeLists.txt | 5 + .../backend/DescriptorSetOffsetArray.h | 101 +++++ .../backend/include/backend/DriverEnums.h | 110 ++++-- filament/backend/include/backend/Handle.h | 30 +- .../backend/include/backend/PipelineState.h | 8 + filament/backend/include/backend/Program.h | 128 +++---- .../backend/include/private/backend/Driver.h | 1 + .../include/private/backend/DriverAPI.inc | 59 ++- .../include/private/backend/DriverApi.h | 11 + .../include/private/backend/HandleAllocator.h | 2 +- filament/backend/src/DriverBase.h | 8 + filament/backend/src/Program.cpp | 46 +-- filament/backend/src/metal/MetalDriver.mm | 110 +++--- filament/backend/src/metal/MetalHandles.h | 10 +- filament/backend/src/metal/MetalHandles.mm | 2 +- filament/backend/src/metal/MetalState.h | 4 - filament/backend/src/noop/NoopDriver.cpp | 30 +- filament/backend/src/opengl/BindingMap.h | 89 +++++ .../backend/src/opengl/GLDescriptorSet.cpp | 301 +++++++++++++++ filament/backend/src/opengl/GLDescriptorSet.h | 143 +++++++ .../src/opengl/GLDescriptorSetLayout.h | 52 +++ filament/backend/src/opengl/OpenGLContext.h | 31 +- filament/backend/src/opengl/OpenGLDriver.cpp | 355 +++++++++--------- filament/backend/src/opengl/OpenGLDriver.h | 34 +- filament/backend/src/opengl/OpenGLProgram.cpp | 187 ++++----- filament/backend/src/opengl/OpenGLProgram.h | 65 ++-- filament/backend/src/vulkan/VulkanDriver.cpp | 47 ++- filament/backend/src/vulkan/VulkanHandles.cpp | 52 +-- filament/backend/src/vulkan/VulkanHandles.h | 6 + filament/backend/test/BackendTest.cpp | 18 +- filament/backend/test/BackendTest.h | 9 +- filament/backend/test/test_Blit.cpp | 18 +- filament/backend/test/test_BufferUpdates.cpp | 120 +++--- filament/backend/test/test_ComputeBasic.cpp | 5 +- filament/backend/test/test_FeedbackLoops.cpp | 70 ++-- filament/backend/test/test_LoadImage.cpp | 131 ++++--- filament/backend/test/test_MipLevels.cpp | 40 +- .../backend/test/test_RenderExternalImage.cpp | 138 ++++--- filament/include/filament/RenderableManager.h | 53 +-- filament/src/MaterialParser.cpp | 235 +++++++----- filament/src/MaterialParser.h | 67 ++-- filament/src/PostProcessManager.cpp | 57 ++- filament/src/PostProcessManager.h | 40 +- filament/src/RenderPass.cpp | 279 +++++++------- filament/src/RenderPass.h | 85 +++-- filament/src/RenderPrimitive.cpp | 2 +- filament/src/RenderPrimitive.h | 8 +- filament/src/RenderableManager.cpp | 11 +- filament/src/RendererUtils.cpp | 8 +- filament/src/ShadowMap.cpp | 18 +- filament/src/ShadowMap.h | 11 +- filament/src/ShadowMapManager.cpp | 6 +- filament/src/ShadowMapManager.h | 4 +- filament/src/components/RenderableManager.cpp | 99 ++--- filament/src/components/RenderableManager.h | 67 ++-- filament/src/details/Engine.cpp | 20 + filament/src/details/Engine.h | 17 + filament/src/details/Material.cpp | 66 ++-- filament/src/details/Material.h | 53 ++- filament/src/details/MaterialInstance.cpp | 128 +++++-- filament/src/details/MaterialInstance.h | 42 +-- filament/src/details/MorphTargetBuffer.cpp | 15 +- filament/src/details/MorphTargetBuffer.h | 34 +- filament/src/details/Renderer.cpp | 29 +- filament/src/details/Scene.h | 4 + filament/src/details/SkinningBuffer.cpp | 25 +- filament/src/details/SkinningBuffer.h | 26 +- filament/src/details/View.cpp | 154 +++++--- filament/src/details/View.h | 52 ++- .../ColorPassDescriptorSet.cpp} | 253 +++++++++---- .../ColorPassDescriptorSet.h} | 55 ++- filament/src/ds/DescriptorSet.cpp | 134 +++++++ filament/src/ds/DescriptorSet.h | 108 ++++++ filament/src/ds/DescriptorSetLayout.cpp | 52 +++ filament/src/ds/DescriptorSetLayout.h | 75 ++++ filament/src/ds/PostProcessDescriptorSet.cpp | 65 ++++ filament/src/ds/PostProcessDescriptorSet.h | 58 +++ .../ShadowMapDescriptorSet.cpp} | 39 +- .../ShadowMapDescriptorSet.h} | 21 +- filament/src/ds/SsrPassDescriptorSet.cpp | 99 +++++ filament/src/ds/SsrPassDescriptorSet.h | 73 ++++ .../TypedBuffer.h} | 13 +- filament/src/ds/TypedUniformBuffer.h | 89 +++++ libs/filabridge/CMakeLists.txt | 3 +- .../include/filament/MaterialChunkType.h | 4 +- .../include/filament/MaterialEnums.h | 3 + .../include/private/filament/DescriptorSets.h | 50 +++ .../include/private/filament/EngineEnums.h | 69 ++-- .../private/filament/SamplerBindingsInfo.h | 52 --- .../private/filament/SamplerInterfaceBlock.h | 10 +- libs/filabridge/src/DescriptorSets.cpp | 209 +++++++++++ libs/filabridge/src/SamplerInterfaceBlock.cpp | 25 +- libs/filamat/CMakeLists.txt | 3 +- libs/filamat/src/GLSLPostProcessor.cpp | 167 ++++---- libs/filamat/src/MaterialBuilder.cpp | 88 ++--- libs/filamat/src/SamplerBindingMap.cpp | 105 ------ libs/filamat/src/SamplerBindingMap.h | 77 ---- .../src/eiff/MaterialInterfaceBlockChunk.cpp | 177 ++++++--- .../src/eiff/MaterialInterfaceBlockChunk.h | 60 +-- libs/filamat/src/shaders/CodeGenerator.cpp | 76 ++-- libs/filamat/src/shaders/CodeGenerator.h | 36 +- libs/filamat/src/shaders/MaterialInfo.h | 3 - libs/filamat/src/shaders/ShaderGenerator.cpp | 156 +++++--- libs/filamat/src/shaders/ShaderGenerator.h | 13 +- libs/filamat/src/shaders/SibGenerator.cpp | 89 ++--- libs/filamat/src/shaders/SibGenerator.h | 5 +- libs/filamat/src/shaders/UibGenerator.cpp | 60 +++ libs/filamat/src/shaders/UibGenerator.h | 47 ++- libs/gltfio/src/AssetLoader.cpp | 72 +++- libs/gltfio/src/FFilamentAsset.h | 6 +- libs/gltfio/src/ResourceLoader.cpp | 24 +- libs/gltfio/src/TangentsJob.h | 1 + .../src/extended/AssetLoaderExtended.cpp | 10 +- libs/gltfio/test/gltfio_test.cpp | 2 +- libs/matdbg/src/TextWriter.cpp | 6 + .../utils/include/utils/FixedCapacityVector.h | 2 +- libs/utils/src/EntityManagerImpl.h | 7 - samples/hellomorphing.cpp | 6 +- samples/skinningtest.cpp | 22 +- shaders/src/ambient_occlusion.fs | 34 +- shaders/src/fog.fs | 2 +- shaders/src/getters.vs | 10 +- shaders/src/light_directional.fs | 2 +- shaders/src/light_indirect.fs | 22 +- shaders/src/light_punctual.fs | 2 +- shaders/src/light_reflections.fs | 6 +- shaders/src/main.fs | 2 +- shaders/src/shading_unlit.fs | 2 +- shaders/src/shadowing.fs | 2 +- 132 files changed, 4860 insertions(+), 2455 deletions(-) create mode 100644 filament/backend/include/backend/DescriptorSetOffsetArray.h create mode 100644 filament/backend/src/opengl/BindingMap.h create mode 100644 filament/backend/src/opengl/GLDescriptorSet.cpp create mode 100644 filament/backend/src/opengl/GLDescriptorSet.h create mode 100644 filament/backend/src/opengl/GLDescriptorSetLayout.h rename filament/src/{PerViewUniforms.cpp => ds/ColorPassDescriptorSet.cpp} (66%) rename filament/src/{PerViewUniforms.h => ds/ColorPassDescriptorSet.h} (78%) create mode 100644 filament/src/ds/DescriptorSet.cpp create mode 100644 filament/src/ds/DescriptorSet.h create mode 100644 filament/src/ds/DescriptorSetLayout.cpp create mode 100644 filament/src/ds/DescriptorSetLayout.h create mode 100644 filament/src/ds/PostProcessDescriptorSet.cpp create mode 100644 filament/src/ds/PostProcessDescriptorSet.h rename filament/src/{PerShadowMapUniforms.cpp => ds/ShadowMapDescriptorSet.cpp} (72%) rename filament/src/{PerShadowMapUniforms.h => ds/ShadowMapDescriptorSet.h} (81%) create mode 100644 filament/src/ds/SsrPassDescriptorSet.cpp create mode 100644 filament/src/ds/SsrPassDescriptorSet.h rename filament/src/{TypedUniformBuffer.h => ds/TypedBuffer.h} (87%) create mode 100644 filament/src/ds/TypedUniformBuffer.h create mode 100644 libs/filabridge/include/private/filament/DescriptorSets.h delete mode 100644 libs/filabridge/include/private/filament/SamplerBindingsInfo.h create mode 100644 libs/filabridge/src/DescriptorSets.cpp delete mode 100644 libs/filamat/src/SamplerBindingMap.cpp delete mode 100644 libs/filamat/src/SamplerBindingMap.h diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index db0255d414e..fcec79f686d 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -258,8 +258,7 @@ Java_com_google_android_filament_RenderableManager_nBuilderSetMorphTargetBufferA int offset, int count) { RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; if (nativeMorphTargetBuffer) { - MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; - builder->morphing(level, primitiveIndex, morphTargetBuffer, offset, count); + builder->morphing(level, primitiveIndex, offset, count); } else { builder->morphing(level, primitiveIndex, offset, count); } @@ -336,16 +335,10 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env, extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_RenderableManager_nSetMorphTargetBufferAt(JNIEnv*, jclass, jlong nativeRenderableManager, jint i, int level, jint primitiveIndex, - jlong nativeMorphTargetBuffer, jint offset, jint count) { + jlong, jint offset, jint count) { RenderableManager *rm = (RenderableManager *) nativeRenderableManager; - if (nativeMorphTargetBuffer) { - MorphTargetBuffer *morphTargetBuffer = (MorphTargetBuffer *) nativeMorphTargetBuffer; - rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level, - (size_t) primitiveIndex, morphTargetBuffer, (size_t) offset, (size_t) count); - } else { - rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level, - (size_t) primitiveIndex, (size_t) offset, (size_t) count); - } + rm->setMorphTargetBufferAt((RenderableManager::Instance) i, (uint8_t) level, + (size_t) primitiveIndex, (size_t) offset, (size_t) count); } extern "C" JNIEXPORT jint JNICALL 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 dfce1af4a16..d2cd70daaab 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 @@ -582,30 +582,6 @@ public Builder morphing(@IntRange(from = 0) int level, return this; } - /** @deprecated */ - @Deprecated - @NonNull - public Builder morphing(@IntRange(from = 0) int level, - @IntRange(from = 0) int primitiveIndex, - @NonNull MorphTargetBuffer morphTargetBuffer, - @IntRange(from = 0) int offset, - @IntRange(from = 0) int count) { - nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, - morphTargetBuffer.getNativeObject(), offset, count); - return this; - } - - /** @deprecated */ - @Deprecated - @NonNull - public Builder morphing(@IntRange(from = 0) int level, - @IntRange(from = 0) int primitiveIndex, - @NonNull MorphTargetBuffer morphTargetBuffer) { - nBuilderSetMorphTargetBufferAt(mNativeBuilder, level, primitiveIndex, - morphTargetBuffer.getNativeObject(), 0, morphTargetBuffer.getVertexCount()); - return this; - } - /** * Adds the Renderable component to an entity. * diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 796acedb7d2..96d1b57a19e 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -75,8 +75,6 @@ set(SRCS src/MaterialInstance.cpp src/MaterialParser.cpp src/MorphTargetBuffer.cpp - src/PerViewUniforms.cpp - src/PerShadowMapUniforms.cpp src/PostProcessManager.cpp src/RenderPass.cpp src/RenderPrimitive.cpp @@ -125,6 +123,12 @@ set(SRCS src/details/Texture.cpp src/details/VertexBuffer.cpp src/details/View.cpp + src/ds/ColorPassDescriptorSet.cpp + src/ds/DescriptorSet.cpp + src/ds/DescriptorSetLayout.cpp + src/ds/PostProcessDescriptorSet.cpp + src/ds/ShadowMapDescriptorSet.cpp + src/ds/SsrPassDescriptorSet.cpp src/fg/Blackboard.cpp src/fg/DependencyGraph.cpp src/fg/FrameGraph.cpp @@ -152,19 +156,16 @@ set(PRIVATE_HDRS src/HwVertexBufferInfoFactory.h src/Intersections.h src/MaterialParser.h - src/PerViewUniforms.h - src/PerShadowMapUniforms.h src/PIDController.h src/PostProcessManager.h - src/RendererUtils.h src/RenderPass.h src/RenderPrimitive.h + src/RendererUtils.h src/ResourceAllocator.h src/ResourceList.h src/ShadowMap.h src/ShadowMapManager.h src/SharedHandle.h - src/TypedUniformBuffer.h src/UniformBuffer.h src/components/CameraManager.h src/components/LightManager.h @@ -192,6 +193,14 @@ set(PRIVATE_HDRS src/details/Texture.h src/details/VertexBuffer.h src/details/View.h + src/downcast.h + src/ds/ColorPassDescriptorSet.h + src/ds/DescriptorSetLayout.h + src/ds/PostProcessDescriptorSet.h + src/ds/ShadowMapDescriptorSet.h + src/ds/SsrPassDescriptorSet.h + src/ds/TypedBuffer.h + src/ds/TypedUniformBuffer.h src/fg/Blackboard.h src/fg/FrameGraph.h src/fg/FrameGraphId.h @@ -209,7 +218,6 @@ set(PRIVATE_HDRS src/materials/fsr/ffx_a.h src/materials/fsr/ffx_fsr1.h src/materials/fsr/ffx_fsr1_mobile.fs - src/downcast.h ) set(MATERIAL_SRCS diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 6a7f66cc2fd..79d8ce967b7 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -12,6 +12,7 @@ set(PUBLIC_HDRS include/backend/AcquiredImage.h include/backend/BufferDescriptor.h include/backend/CallbackHandler.h + include/backend/DescriptorSetOffsetArray.h include/backend/DriverApiForward.h include/backend/DriverEnums.h include/backend/Handle.h @@ -69,9 +70,13 @@ set(PRIVATE_HDRS if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3) list(APPEND SRCS include/backend/platforms/OpenGLPlatform.h + src/opengl/BindingMap.h src/opengl/gl_headers.cpp src/opengl/gl_headers.h src/opengl/GLBufferObject.h + src/opengl/GLDescriptorSet.cpp + src/opengl/GLDescriptorSet.h + src/opengl/GLDescriptorSetLayout.h src/opengl/GLTexture.h src/opengl/GLUtils.cpp src/opengl/GLUtils.h diff --git a/filament/backend/include/backend/DescriptorSetOffsetArray.h b/filament/backend/include/backend/DescriptorSetOffsetArray.h new file mode 100644 index 00000000000..3c7b85664bc --- /dev/null +++ b/filament/backend/include/backend/DescriptorSetOffsetArray.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H +#define TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H + +#include + +#include +#include + +#include +#include + + +namespace filament::backend { + +void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept; + +class DescriptorSetOffsetArray { +public: + using value_type = uint32_t; + using reference = value_type&; + using const_reference = value_type const&; + using size_type = uint32_t; + using difference_type = int32_t; + using pointer = value_type*; + using const_pointer = value_type const*; + using iterator = pointer; + using const_iterator = const_pointer; + + DescriptorSetOffsetArray() noexcept = default; + + ~DescriptorSetOffsetArray() noexcept = default; + + DescriptorSetOffsetArray(size_type size, DriverApi& driver) noexcept { + mOffsets = (value_type *)allocateFromCommandStream(driver, + size * sizeof(value_type), alignof(value_type)); + std::uninitialized_fill_n(mOffsets, size, 0); + } + + DescriptorSetOffsetArray(std::initializer_list list, DriverApi& driver) noexcept { + mOffsets = (value_type *)allocateFromCommandStream(driver, + list.size() * sizeof(value_type), alignof(value_type)); + std::uninitialized_copy(list.begin(), list.end(), mOffsets); + } + + DescriptorSetOffsetArray(DescriptorSetOffsetArray const&) = delete; + DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray const&) = delete; + + DescriptorSetOffsetArray(DescriptorSetOffsetArray&& rhs) noexcept + : mOffsets(rhs.mOffsets) { + rhs.mOffsets = nullptr; + } + + DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray&& rhs) noexcept { + if (this != &rhs) { + mOffsets = rhs.mOffsets; + rhs.mOffsets = nullptr; + } + return *this; + } + + bool empty() const noexcept { return mOffsets == nullptr; } + + value_type* data() noexcept { return mOffsets; } + const value_type* data() const noexcept { return mOffsets; } + + + reference operator[](size_type n) noexcept { + return *(data() + n); + } + + const_reference operator[](size_type n) const noexcept { + return *(data() + n); + } + + void clear() noexcept { + mOffsets = nullptr; + } + +private: + value_type *mOffsets = nullptr; +}; + +} // namespace filament::backend + +#endif //TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index d69a9991d28..b01adc7ad09 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -19,13 +19,16 @@ #ifndef TNT_FILAMENT_BACKEND_DRIVERENUMS_H #define TNT_FILAMENT_BACKEND_DRIVERENUMS_H -#include #include // Because we define ERROR in the FenceStatus enum. #include #include +#include +#include #include +#include +#include #include #include @@ -97,6 +100,8 @@ static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guarantee static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3. static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects. static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES. +static constexpr size_t MAX_DESCRIPTOR_SET_COUNT = 4; // This is guaranteed by Vulkan. +static constexpr size_t MAX_DESCRIPTOR_COUNT = 64; // per set static constexpr size_t MAX_PUSH_CONSTANT_COUNT = 32; // Vulkan 1.1 spec allows for 128-byte // of push constant (we assume 4-byte @@ -191,6 +196,60 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag } } +enum class ShaderStage : uint8_t { + VERTEX = 0, + FRAGMENT = 1, + COMPUTE = 2 +}; + +static constexpr size_t PIPELINE_STAGE_COUNT = 2; +enum class ShaderStageFlags : uint8_t { + NONE = 0, + VERTEX = 0x1, + FRAGMENT = 0x2, + COMPUTE = 0x4, + ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE +}; + +static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { + switch (type) { + case ShaderStage::VERTEX: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); + case ShaderStage::FRAGMENT: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT)); + case ShaderStage::COMPUTE: + return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE)); + } +} + +enum class DescriptorType : uint8_t { + UNIFORM_BUFFER, + SHADER_STORAGE_BUFFER, + SAMPLER, +}; + +enum class DescriptorFlags : uint8_t { + NONE = 0x00, + DYNAMIC_OFFSET = 0x01 +}; + +using descriptor_set_t = uint8_t; + +using descriptor_binding_t = uint8_t; + +struct DescriptorSetLayoutBinding { + DescriptorType type; + ShaderStageFlags stageFlags; + descriptor_binding_t binding; + DescriptorFlags flags; + uint16_t count; +}; + +struct DescriptorSetLayout { + utils::FixedCapacityVector bindings; +}; + + /** * Bitmask for selecting render buffers */ @@ -270,15 +329,6 @@ enum class FenceStatus : int8_t { TIMEOUT_EXPIRED = 1, //!< wait()'s timeout expired. The Fence condition is not satisfied. }; -/** - * Status codes for sync objects - */ -enum class SyncStatus : int8_t { - ERROR = -1, //!< An error occurred. The Sync is not signaled. - SIGNALED = 0, //!< The Sync is signaled. - NOT_SIGNALED = 1, //!< The Sync is not signaled yet -}; - static constexpr uint64_t FENCE_WAIT_FOR_EVER = uint64_t(-1); /** @@ -887,6 +937,9 @@ struct SamplerParams { // NOLINT struct EqualTo { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { + assert_invariant(lhs.padding0 == 0); + assert_invariant(lhs.padding1 == 0); + assert_invariant(lhs.padding2 == 0); auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); return *pLhs == *pRhs; @@ -895,6 +948,9 @@ struct SamplerParams { // NOLINT struct LessThan { bool operator()(SamplerParams lhs, SamplerParams rhs) const noexcept { + assert_invariant(lhs.padding0 == 0); + assert_invariant(lhs.padding1 == 0); + assert_invariant(lhs.padding2 == 0); auto* pLhs = reinterpret_cast(reinterpret_cast(&lhs)); auto* pRhs = reinterpret_cast(reinterpret_cast(&rhs)); return *pLhs == *pRhs; @@ -902,6 +958,12 @@ struct SamplerParams { // NOLINT }; private: + friend inline bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept { + return SamplerParams::EqualTo{}(lhs, rhs); + } + friend inline bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept { + return !SamplerParams::EqualTo{}(lhs, rhs); + } friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept { return SamplerParams::LessThan{}(lhs, rhs); } @@ -1069,32 +1131,6 @@ struct RasterState { * \privatesection */ -enum class ShaderStage : uint8_t { - VERTEX = 0, - FRAGMENT = 1, - COMPUTE = 2 -}; - -static constexpr size_t PIPELINE_STAGE_COUNT = 2; -enum class ShaderStageFlags : uint8_t { - NONE = 0, - VERTEX = 0x1, - FRAGMENT = 0x2, - COMPUTE = 0x4, - ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE -}; - -static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept { - switch (type) { - case ShaderStage::VERTEX: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX)); - case ShaderStage::FRAGMENT: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT)); - case ShaderStage::COMPUTE: - return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE)); - } -} - /** * Selects which buffers to clear at the beginning of the render pass, as well as which buffers * can be discarded at the beginning and end of the render pass. @@ -1259,6 +1295,8 @@ template<> struct utils::EnableBitMaskOperators struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; template<> struct utils::EnableBitMaskOperators : public std::true_type {}; template<> struct utils::EnableBitMaskOperators diff --git a/filament/backend/include/backend/Handle.h b/filament/backend/include/backend/Handle.h index c54e9609cef..2cf52244149 100644 --- a/filament/backend/include/backend/Handle.h +++ b/filament/backend/include/backend/Handle.h @@ -41,6 +41,8 @@ struct HwTexture; struct HwTimerQuery; struct HwVertexBufferInfo; struct HwVertexBuffer; +struct HwDescriptorSetLayout; +struct HwDescriptorSet; /* * A handle to a backend resource. HandleBase is for internal use only. @@ -130,19 +132,21 @@ struct Handle : public HandleBase { // Types used by the command stream // (we use this renaming because the macro-system doesn't deal well with "<" and ">") -using BufferObjectHandle = Handle; -using FenceHandle = Handle; -using IndexBufferHandle = Handle; -using ProgramHandle = Handle; -using RenderPrimitiveHandle = Handle; -using RenderTargetHandle = Handle; -using SamplerGroupHandle = Handle; -using StreamHandle = Handle; -using SwapChainHandle = Handle; -using TextureHandle = Handle; -using TimerQueryHandle = Handle; -using VertexBufferHandle = Handle; -using VertexBufferInfoHandle = Handle; +using BufferObjectHandle = Handle; +using FenceHandle = Handle; +using IndexBufferHandle = Handle; +using ProgramHandle = Handle; +using RenderPrimitiveHandle = Handle; +using RenderTargetHandle = Handle; +using SamplerGroupHandle = Handle; +using StreamHandle = Handle; +using SwapChainHandle = Handle; +using TextureHandle = Handle; +using TimerQueryHandle = Handle; +using VertexBufferHandle = Handle; +using VertexBufferInfoHandle = Handle; +using DescriptorSetLayoutHandle = Handle; +using DescriptorSetHandle = Handle; } // namespace filament::backend diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h index 220d04bbf26..051a381a521 100644 --- a/filament/backend/include/backend/PipelineState.h +++ b/filament/backend/include/backend/PipelineState.h @@ -22,15 +22,23 @@ #include +#include + #include namespace filament::backend { //! \privatesection +struct PipelineLayout { + using SetLayout = std::array, MAX_DESCRIPTOR_SET_COUNT>; + SetLayout setLayout; // 16 +}; + struct PipelineState { Handle program; // 4 Handle vertexBufferInfo; // 4 + PipelineLayout pipelineLayout; // 16 RasterState rasterState; // 4 StencilState stencilState; // 12 PolygonOffset polygonOffset; // 8 diff --git a/filament/backend/include/backend/Program.h b/filament/backend/include/backend/Program.h index 7cec72cd0c2..f8e9d50eb87 100644 --- a/filament/backend/include/backend/Program.h +++ b/filament/backend/include/backend/Program.h @@ -24,9 +24,11 @@ #include -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers -#include // FIXME: STL headers are not allowed in public headers +#include +#include +#include +#include +#include #include #include @@ -40,29 +42,36 @@ class Program { static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT; static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT; - struct Sampler { - utils::CString name = {}; // name of the sampler in the shader - uint32_t binding = 0; // binding point of the sampler in the shader + struct Descriptor { + utils::CString name; + backend::DescriptorType type; + backend::descriptor_binding_t binding; }; - struct SamplerGroupData { - utils::FixedCapacityVector samplers; - ShaderStageFlags stageFlags = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS; + struct SpecializationConstant { + using Type = std::variant; + uint32_t id; // id set in glsl + Type value; // value and type }; - struct Uniform { + struct Uniform { // For ES2 support utils::CString name; // full qualified name of the uniform field uint16_t offset; // offset in 'uint32_t' into the uniform buffer uint8_t size; // >1 for arrays UniformType type; // uniform type }; - using UniformBlockInfo = std::array; - using UniformInfo = utils::FixedCapacityVector; - using SamplerGroupInfo = std::array; + using DescriptorBindingsInfo = utils::FixedCapacityVector; + using DescriptorSetInfo = std::array; + using SpecializationConstantsInfo = utils::FixedCapacityVector; using ShaderBlob = utils::FixedCapacityVector; using ShaderSource = std::array; + using AttributesInfo = utils::FixedCapacityVector>; + using UniformInfo = utils::FixedCapacityVector; + using BindingUniformsInfo = utils::FixedCapacityVector< + std::tuple>; + Program() noexcept; Program(const Program& rhs) = delete; @@ -79,43 +88,19 @@ class Program { Program& diagnostics(utils::CString const& name, utils::Invocable&& logger); - // sets one of the program's shader (e.g. vertex, fragment) + // Sets one of the program's shader (e.g. vertex, fragment) // string-based shaders are null terminated, consequently the size parameter must include the // null terminating character. Program& shader(ShaderStage shader, void const* data, size_t size); - // sets the language of the shader sources provided with shader() (defaults to ESSL3) + // Sets the language of the shader sources provided with shader() (defaults to ESSL3) Program& shaderLanguage(ShaderLanguage shaderLanguage); - // Note: This is only needed for GLES3.0 backends, because the layout(binding=) syntax is - // not permitted in glsl. The backend needs a way to associate a uniform block - // to a binding point. - Program& uniformBlockBindings( - utils::FixedCapacityVector> const& uniformBlockBindings) noexcept; - - // Note: This is only needed for GLES2.0, this is used to emulate UBO. This function tells - // the program everything it needs to know about the uniforms at a given binding - Program& uniforms(uint32_t index, UniformInfo const& uniforms) noexcept; - - // Note: This is only needed for GLES2.0. - Program& attributes( - utils::FixedCapacityVector> attributes) noexcept; - - // sets the 'bindingPoint' sampler group descriptor for this program. - // 'samplers' can be destroyed after this call. - // This effectively associates a set of (BindingPoints, index) to a texture unit in the shader. - // Or more precisely, what layout(binding=) is set to in GLSL. - Program& setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags, - Sampler const* samplers, size_t count) noexcept; - - struct SpecializationConstant { - using Type = std::variant; - uint32_t id; // id set in glsl - Type value; // value and type - }; + // Descriptor binding (set, binding, type -> shader name) info + Program& descriptorBindings(backend::descriptor_set_t set, + DescriptorBindingsInfo descriptorBindings) noexcept; - Program& specializationConstants( - utils::FixedCapacityVector specConstants) noexcept; + Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept; struct PushConstant { utils::CString name; @@ -129,33 +114,40 @@ class Program { Program& multiview(bool multiview) noexcept; - ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } - ShaderSource& getShadersSource() noexcept { return mShadersSource; } - - UniformBlockInfo const& getUniformBlockBindings() const noexcept { return mUniformBlocks; } - UniformBlockInfo& getUniformBlockBindings() noexcept { return mUniformBlocks; } - - SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; } - SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; } + // For ES2 support only... + Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept; + Program& attributes(AttributesInfo attributes) noexcept; - auto const& getBindingUniformInfo() const { return mBindingUniformInfo; } - auto& getBindingUniformInfo() { return mBindingUniformInfo; } + // + // Getters for program construction... + // - auto const& getAttributes() const { return mAttributes; } - auto& getAttributes() { return mAttributes; } + ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } + ShaderSource& getShadersSource() noexcept { return mShadersSource; } utils::CString const& getName() const noexcept { return mName; } utils::CString& getName() noexcept { return mName; } auto const& getShaderLanguage() const { return mShaderLanguage; } - utils::FixedCapacityVector const& getSpecializationConstants() const noexcept { + uint64_t getCacheId() const noexcept { return mCacheId; } + + bool isMultiview() const noexcept { return mMultiview; } + + CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } + + SpecializationConstantsInfo const& getSpecializationConstants() const noexcept { return mSpecializationConstants; } - utils::FixedCapacityVector& getSpecializationConstants() noexcept { + + SpecializationConstantsInfo& getSpecializationConstants() noexcept { return mSpecializationConstants; } + DescriptorSetInfo& getDescriptorBindings() noexcept { + return mDescriptorBindings; + } + utils::FixedCapacityVector const& getPushConstants( ShaderStage stage) const noexcept { return mPushConstants[static_cast(stage)]; @@ -165,27 +157,29 @@ class Program { return mPushConstants[static_cast(stage)]; } - uint64_t getCacheId() const noexcept { return mCacheId; } - - bool isMultiview() const noexcept { return mMultiview; } + auto const& getBindingUniformInfo() const { return mBindingUniformsInfo; } + auto& getBindingUniformInfo() { return mBindingUniformsInfo; } - CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; } + auto const& getAttributes() const { return mAttributes; } + auto& getAttributes() { return mAttributes; } private: friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder); - UniformBlockInfo mUniformBlocks = {}; - SamplerGroupInfo mSamplerGroups = {}; ShaderSource mShadersSource; ShaderLanguage mShaderLanguage = ShaderLanguage::ESSL3; utils::CString mName; uint64_t mCacheId{}; + CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; utils::Invocable mLogger; - utils::FixedCapacityVector mSpecializationConstants; + SpecializationConstantsInfo mSpecializationConstants; std::array, SHADER_TYPE_COUNT> mPushConstants; - utils::FixedCapacityVector> mAttributes; - std::array mBindingUniformInfo; - CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; + DescriptorSetInfo mDescriptorBindings; + + // For ES2 support only + AttributesInfo mAttributes; + BindingUniformsInfo mBindingUniformsInfo; + // Indicates the current engine was initialized with multiview stereo, and the variant for this // program contains STE flag. This will be referred later for the OpenGL shader compiler to // determine whether shader code replacement for the num_views should be performed. diff --git a/filament/backend/include/private/backend/Driver.h b/filament/backend/include/private/backend/Driver.h index 527052378e6..1fe16446813 100644 --- a/filament/backend/include/private/backend/Driver.h +++ b/filament/backend/include/private/backend/Driver.h @@ -18,6 +18,7 @@ #define TNT_FILAMENT_BACKEND_PRIVATE_DRIVER_H #include +#include #include #include #include diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index b92a07f37b4..9575b1b8e7c 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -257,25 +257,54 @@ DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChainHeadless, DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery) +DECL_DRIVER_API_R_N(backend::DescriptorSetLayoutHandle, createDescriptorSetLayout, + backend::DescriptorSetLayout&&, info) + +DECL_DRIVER_API_R_N(backend::DescriptorSetHandle, createDescriptorSet, + backend::DescriptorSetLayoutHandle, dslh) + +DECL_DRIVER_API_N(updateDescriptorSetBuffer, + backend::DescriptorSetHandle, dsh, + backend::descriptor_binding_t, binding, + backend::BufferObjectHandle, boh, + uint32_t, offset, + uint32_t, size +) + +DECL_DRIVER_API_N(updateDescriptorSetTexture, + backend::DescriptorSetHandle, dsh, + backend::descriptor_binding_t, binding, + backend::TextureHandle, th, + SamplerParams, params +) + +DECL_DRIVER_API_N(bindDescriptorSet, + backend::DescriptorSetHandle, dsh, + backend::descriptor_set_t, set, + backend::DescriptorSetOffsetArray&&, offsets +) + /* * Destroying driver objects * ------------------------- */ -DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh) -DECL_DRIVER_API_N(destroyVertexBufferInfo,backend::VertexBufferInfoHandle, vbih) -DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh) -DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh) -DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph) -DECL_DRIVER_API_N(destroyProgram, backend::ProgramHandle, ph) -DECL_DRIVER_API_N(destroySamplerGroup, backend::SamplerGroupHandle, sbh) -DECL_DRIVER_API_N(destroyTexture, backend::TextureHandle, th) -DECL_DRIVER_API_N(destroyRenderTarget, backend::RenderTargetHandle, rth) -DECL_DRIVER_API_N(destroySwapChain, backend::SwapChainHandle, sch) -DECL_DRIVER_API_N(destroyStream, backend::StreamHandle, sh) -DECL_DRIVER_API_N(destroyTimerQuery, backend::TimerQueryHandle, sh) -DECL_DRIVER_API_N(destroyFence, backend::FenceHandle, fh) +DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh) +DECL_DRIVER_API_N(destroyVertexBufferInfo, backend::VertexBufferInfoHandle, vbih) +DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh) +DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh) +DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph) +DECL_DRIVER_API_N(destroyProgram, backend::ProgramHandle, ph) +DECL_DRIVER_API_N(destroySamplerGroup, backend::SamplerGroupHandle, sbh) +DECL_DRIVER_API_N(destroyTexture, backend::TextureHandle, th) +DECL_DRIVER_API_N(destroyRenderTarget, backend::RenderTargetHandle, rth) +DECL_DRIVER_API_N(destroySwapChain, backend::SwapChainHandle, sch) +DECL_DRIVER_API_N(destroyStream, backend::StreamHandle, sh) +DECL_DRIVER_API_N(destroyTimerQuery, backend::TimerQueryHandle, sh) +DECL_DRIVER_API_N(destroyFence, backend::FenceHandle, fh) +DECL_DRIVER_API_N(destroyDescriptorSetLayout, backend::DescriptorSetLayoutHandle, dslh) +DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, dsh) /* * Synchronous APIs @@ -425,10 +454,6 @@ DECL_DRIVER_API_N(bindBufferRange, uint32_t, offset, uint32_t, size) -DECL_DRIVER_API_N(unbindBuffer, - BufferObjectBinding, bindingType, - uint32_t, index) - DECL_DRIVER_API_N(bindSamplers, uint32_t, index, backend::SamplerGroupHandle, sbh) diff --git a/filament/backend/include/private/backend/DriverApi.h b/filament/backend/include/private/backend/DriverApi.h index 68d997a439d..dfd1a964d93 100644 --- a/filament/backend/include/private/backend/DriverApi.h +++ b/filament/backend/include/private/backend/DriverApi.h @@ -18,6 +18,17 @@ #define TNT_FILAMENT_BACKEND_PRIVATE_DRIVERAPI_H #include "backend/DriverApiForward.h" + #include "private/backend/CommandStream.h" +#include + +namespace filament::backend { + +inline void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept { + return driver.allocate(size, alignment); +} + +} // namespace filament::backend + #endif // TNT_FILAMENT_BACKEND_PRIVATE_DRIVERAPI_H diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index afc6dc924e0..e5eaf6c1a8a 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -37,7 +37,7 @@ #include #include -#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB +#define HandleAllocatorGL HandleAllocator<32, 96, 136> // ~4520 / pool / MiB #define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB #define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB diff --git a/filament/backend/src/DriverBase.h b/filament/backend/src/DriverBase.h index 24acfe60e1b..c7c1a204155 100644 --- a/filament/backend/src/DriverBase.h +++ b/filament/backend/src/DriverBase.h @@ -101,6 +101,14 @@ struct HwProgram : public HwBase { HwProgram() noexcept = default; }; +struct HwDescriptorSetLayout : public HwBase { + HwDescriptorSetLayout() noexcept = default; +}; + +struct HwDescriptorSet : public HwBase { + HwDescriptorSet() noexcept = default; +}; + struct HwSamplerGroup : public HwBase { HwSamplerGroup() noexcept = default; }; diff --git a/filament/backend/src/Program.cpp b/filament/backend/src/Program.cpp index 53bfa5a7a15..bb4249f82a6 100644 --- a/filament/backend/src/Program.cpp +++ b/filament/backend/src/Program.cpp @@ -14,7 +14,18 @@ * limitations under the License. */ -#include "backend/Program.h" +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include namespace filament::backend { @@ -52,41 +63,24 @@ Program& Program::shaderLanguage(ShaderLanguage shaderLanguage) { return *this; } -Program& Program::uniformBlockBindings( - FixedCapacityVector> const& uniformBlockBindings) noexcept { - for (auto const& item : uniformBlockBindings) { - assert_invariant(item.second < UNIFORM_BINDING_COUNT); - mUniformBlocks[item.second] = item.first; - } +Program& Program::descriptorBindings(backend::descriptor_set_t set, + DescriptorBindingsInfo descriptorBindings) noexcept { + mDescriptorBindings[set] = std::move(descriptorBindings); return *this; } -Program& Program::uniforms(uint32_t index, UniformInfo const& uniforms) noexcept { - assert_invariant(index < UNIFORM_BINDING_COUNT); - mBindingUniformInfo[index] = uniforms; +Program& Program::uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept { + mBindingUniformsInfo.reserve(mBindingUniformsInfo.capacity() + 1); + mBindingUniformsInfo.emplace_back(index, std::move(name), std::move(uniforms)); return *this; } - -Program& Program::attributes( - utils::FixedCapacityVector> attributes) noexcept { +Program& Program::attributes(AttributesInfo attributes) noexcept { mAttributes = std::move(attributes); return *this; } -Program& Program::setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags, - const Program::Sampler* samplers, size_t count) noexcept { - auto& groupData = mSamplerGroups[bindingPoint]; - groupData.stageFlags = stageFlags; - auto& samplerList = groupData.samplers; - samplerList.reserve(count); - samplerList.resize(count); - std::copy_n(samplers, count, samplerList.data()); - return *this; -} - -Program& Program::specializationConstants( - FixedCapacityVector specConstants) noexcept { +Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept { mSpecializationConstants = std::move(specConstants); return *this; } diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 58852a67143..caabe35834c 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -289,6 +289,21 @@ #endif } +void MetalDriver::updateDescriptorSetBuffer( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::BufferObjectHandle boh, + uint32_t offset, + uint32_t size) { +} + +void MetalDriver::updateDescriptorSetTexture( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::TextureHandle th, + SamplerParams params) { +} + void MetalDriver::flush(int) { FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext)) << "flush must be called outside of a render pass."; @@ -470,6 +485,14 @@ // nothing to do, timer query was constructed in createTimerQueryS } +void MetalDriver::createDescriptorSetLayoutR(Handle dslh, + DescriptorSetLayout&& info) { +} + +void MetalDriver::createDescriptorSetR(Handle dsh, + Handle dslh) { +} + Handle MetalDriver::createVertexBufferInfoS() noexcept { return alloc_handle(); } @@ -538,6 +561,14 @@ return alloc_and_construct_handle(); } +Handle MetalDriver::createDescriptorSetLayoutS() noexcept { + return alloc_and_construct_handle(); +} + +Handle MetalDriver::createDescriptorSetS() noexcept { + return alloc_and_construct_handle(); +} + void MetalDriver::destroyVertexBufferInfo(Handle vbih) { if (vbih) { destruct_handle(vbih); @@ -647,6 +678,12 @@ } } +void MetalDriver::destroyDescriptorSetLayout(Handle dslh) { +} + +void MetalDriver::destroyDescriptorSet(Handle dsh) { +} + void MetalDriver::terminate() { // Terminate any outstanding MetalTextures. while (!mContext->texturesToDestroy.empty()) { @@ -1216,45 +1253,6 @@ } } -void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) { - - assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE || - bindingType == BufferObjectBinding::UNIFORM); - - switch (bindingType) { - default: - case BufferObjectBinding::UNIFORM: { - assert_invariant(index < Program::UNIFORM_BINDING_COUNT); - auto* currentBo = mContext->uniformState[index].buffer; - if (currentBo) { - currentBo->boundUniformBuffers.unset(index); - } - mContext->uniformState[index] = BufferState { - .buffer = nullptr, - .offset = 0, - .bound = false - }; - - break; - } - - case BufferObjectBinding::SHADER_STORAGE: { - assert_invariant(index < MAX_SSBO_COUNT); - auto* currentBo = mContext->ssboState[index].buffer; - if (currentBo) { - currentBo->boundSsbos.unset(index); - } - mContext->ssboState[index] = BufferState { - .buffer = nullptr, - .offset = 0, - .bound = false - }; - - break; - } - } -} - void MetalDriver::bindSamplers(uint32_t index, Handle sbh) { auto sb = handle_cast(sbh); mContext->samplerBindings[index] = sb; @@ -1799,10 +1797,10 @@ if (!samplerGroup) { continue; } - const auto& stageFlags = program->getSamplerGroupInfo()[s].stageFlags; - if (stageFlags == ShaderStageFlags::NONE) { - continue; - } +// const auto& stageFlags = program->getSamplerGroupInfo()[s].stageFlags; +// if (stageFlags == ShaderStageFlags::NONE) { +// continue; +// } auto iter = mContext->finalizedSamplerGroups.find(samplerGroup); if (iter == mContext->finalizedSamplerGroups.end()) { @@ -1812,16 +1810,16 @@ assert_invariant(samplerGroup->getArgumentBuffer()); - if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::VERTEX)) { - [mContext->currentRenderPassEncoder setVertexBuffer:samplerGroup->getArgumentBuffer() - offset:samplerGroup->getArgumentBufferOffset() - atIndex:(SAMPLER_GROUP_BINDING_START + s)]; - } - if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::FRAGMENT)) { - [mContext->currentRenderPassEncoder setFragmentBuffer:samplerGroup->getArgumentBuffer() - offset:samplerGroup->getArgumentBufferOffset() - atIndex:(SAMPLER_GROUP_BINDING_START + s)]; - } +// if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::VERTEX)) { +// [mContext->currentRenderPassEncoder setVertexBuffer:samplerGroup->getArgumentBuffer() +// offset:samplerGroup->getArgumentBufferOffset() +// atIndex:(SAMPLER_GROUP_BINDING_START + s)]; +// } +// if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::FRAGMENT)) { +// [mContext->currentRenderPassEncoder setFragmentBuffer:samplerGroup->getArgumentBuffer() +// offset:samplerGroup->getArgumentBufferOffset() +// atIndex:(SAMPLER_GROUP_BINDING_START + s)]; +// } } } @@ -1862,6 +1860,12 @@ atIndex:ZERO_VERTEX_BUFFER_BINDING]; } +void MetalDriver::bindDescriptorSet( + backend::DescriptorSetHandle dsh, + backend::descriptor_set_t set, + backend::DescriptorSetOffsetArray&& offsets) { +} + void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) << "draw() without a valid command encoder."; diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 4d6f864639f..4f7c0eb7b0f 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -200,12 +200,12 @@ class MetalProgram : public HwProgram { MetalProgram(MetalContext& context, Program&& program) noexcept; const MetalShaderCompiler::MetalFunctionBundle& getFunctions(); - const Program::SamplerGroupInfo& getSamplerGroupInfo() { return samplerGroupInfo; } +// const Program::SamplerGroupInfo& getSamplerGroupInfo() { return samplerGroupInfo; } private: void initialize(); - Program::SamplerGroupInfo samplerGroupInfo; +// Program::SamplerGroupInfo samplerGroupInfo; MetalContext& mContext; MetalShaderCompiler::MetalFunctionBundle mFunctionBundle; MetalShaderCompiler::program_token_t mToken; @@ -547,6 +547,12 @@ struct MetalTimerQuery : public HwTimerQuery { std::shared_ptr status; }; +struct MetalDescriptorSetLayout : public HwDescriptorSetLayout { +}; + +struct MetalDescriptorSet : public HwDescriptorSet { +}; + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 3d56956387f..991c7ed03d8 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -482,7 +482,7 @@ static void func(void* user) { // Save this program's SamplerGroupInfo, it's used during draw calls to bind sampler groups to // the appropriate stage(s). - samplerGroupInfo = program.getSamplerGroupInfo(); +// samplerGroupInfo = program.getSamplerGroupInfo(); mToken = context.shaderCompiler->createProgram(program.getName(), std::move(program)); assert_invariant(mToken); diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index 83579cb5d50..14733730377 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -33,10 +33,6 @@ namespace filament { namespace backend { -inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) { - return SamplerParams::EqualTo{}(lhs, rhs); -} - // Rasterization Bindings // ---------------------- // Bindings Buffer name Count diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index ea5c526402d..ba7d6b92d5b 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -111,6 +111,12 @@ void NoopDriver::destroyStream(Handle sh) { void NoopDriver::destroyTimerQuery(Handle tqh) { } +void NoopDriver::destroyDescriptorSetLayout(Handle tqh) { +} + +void NoopDriver::destroyDescriptorSet(Handle tqh) { +} + Handle NoopDriver::createStreamNative(void* nativeStream) { return {}; } @@ -306,9 +312,6 @@ void NoopDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index Handle ubh, uint32_t offset, uint32_t size) { } -void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) { -} - void NoopDriver::bindSamplers(uint32_t index, Handle sbh) { } @@ -388,4 +391,25 @@ void NoopDriver::endTimerQuery(Handle tqh) { void NoopDriver::resetState(int) { } +void NoopDriver::updateDescriptorSetBuffer( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::BufferObjectHandle boh, + uint32_t offset, + uint32_t size) { +} + +void NoopDriver::updateDescriptorSetTexture( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::TextureHandle th, + SamplerParams params) { +} + +void NoopDriver::bindDescriptorSet( + backend::DescriptorSetHandle dsh, + backend::descriptor_set_t set, + backend::DescriptorSetOffsetArray&& offsets) { +} + } // namespace filament diff --git a/filament/backend/src/opengl/BindingMap.h b/filament/backend/src/opengl/BindingMap.h new file mode 100644 index 00000000000..b4f53d1b5da --- /dev/null +++ b/filament/backend/src/opengl/BindingMap.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_OPENGL_BINDINGMAP_H +#define TNT_FILAMENT_BACKEND_OPENGL_BINDINGMAP_H + +#include + +#include "gl_headers.h" + +#include +#include + +#include + +#include +#include +#include + +namespace filament::backend { + +class BindingMap { + struct CompressedBinding { + // this is in fact a GLuint, but we only want 8-bits + uint8_t binding : 7; + uint8_t sampler : 1; + }; + + CompressedBinding (*mStorage)[MAX_DESCRIPTOR_COUNT]; + + utils::bitset64 mActiveDescriptors[MAX_DESCRIPTOR_SET_COUNT]; + +public: + BindingMap() noexcept + : mStorage(new (std::nothrow) CompressedBinding[MAX_DESCRIPTOR_SET_COUNT][MAX_DESCRIPTOR_COUNT]) { +#ifndef NDEBUG + memset(mStorage, 0xFF, sizeof(CompressedBinding[MAX_DESCRIPTOR_SET_COUNT][MAX_DESCRIPTOR_COUNT])); +#endif + } + + ~BindingMap() noexcept { + delete [] mStorage; + } + + BindingMap(BindingMap const&) noexcept = delete; + BindingMap(BindingMap&&) noexcept = delete; + BindingMap& operator=(BindingMap const&) noexcept = delete; + BindingMap& operator=(BindingMap&&) noexcept = delete; + + struct Binding { + GLuint binding; + DescriptorType type; + }; + + void insert(descriptor_set_t set, descriptor_binding_t binding, Binding entry) noexcept { + assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT); + assert_invariant(binding < MAX_DESCRIPTOR_COUNT); + assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now + mStorage[set][binding] = { (uint8_t)entry.binding, entry.type == DescriptorType::SAMPLER }; + mActiveDescriptors[set].set(binding); + } + + GLuint get(descriptor_set_t set, descriptor_binding_t binding) const noexcept { + assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT); + assert_invariant(binding < MAX_DESCRIPTOR_COUNT); + return mStorage[set][binding].binding; + } + + utils::bitset64 getActiveDescriptors(descriptor_set_t set) const noexcept { + return mActiveDescriptors[set]; + } +}; + +} // namespace filament::backend + +#endif //TNT_FILAMENT_BACKEND_OPENGL_BINDINGMAP_H diff --git a/filament/backend/src/opengl/GLDescriptorSet.cpp b/filament/backend/src/opengl/GLDescriptorSet.cpp new file mode 100644 index 00000000000..8669236ca27 --- /dev/null +++ b/filament/backend/src/opengl/GLDescriptorSet.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLDescriptorSet.h" + +#include "GLBufferObject.h" +#include "GLDescriptorSetLayout.h" +#include "GLTexture.h" +#include "GLUtils.h" +#include "OpenGLContext.h" +#include "OpenGLProgram.h" + +#include "gl_headers.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace filament::backend { + +GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh, + GLDescriptorSetLayout const* layout) noexcept + : descriptors(layout->maxDescriptorBinding + 1), + dslh(dslh) { + + // We have allocated enough storage for all descriptors. Now allocate the empty descriptor + // themselves. + for (auto const& entry : layout->bindings) { + size_t const index = entry.binding; + + // now we'll initialize the alternative for each way we can handle this descriptor. + auto& desc = descriptors[index].desc; + switch (entry.type) { + case DescriptorType::UNIFORM_BUFFER: { + // A uniform buffer can have dynamic offsets or not and have special handling for + // ES2 (where we need to emulate it). That's four alternatives. + bool const dynamicOffset = any(entry.flags & DescriptorFlags::DYNAMIC_OFFSET); + dynamicBuffers.set(index, dynamicOffset); + if (UTILS_UNLIKELY(gl.isES2())) { + dynamicBufferCount++; + desc.emplace(dynamicOffset); + } else { + auto const type = GLUtils::getBufferBindingType(BufferObjectBinding::UNIFORM); + if (dynamicOffset) { + dynamicBufferCount++; + desc.emplace(type); + } else { + desc.emplace(type); + } + } + break; + } + case DescriptorType::SHADER_STORAGE_BUFFER: { + // shader storage buffers are not supported on ES2, So that's two alternatives. + bool const dynamicOffset = any(entry.flags & DescriptorFlags::DYNAMIC_OFFSET); + dynamicBuffers.set(index, dynamicOffset); + auto const type = GLUtils::getBufferBindingType(BufferObjectBinding::SHADER_STORAGE); + if (dynamicOffset) { + dynamicBufferCount++; + desc.emplace(type); + } else { + desc.emplace(type); + } + break; + } + case DescriptorType::SAMPLER: + if (UTILS_UNLIKELY(gl.isES2())) { + desc.emplace(); + } else { + const bool anisotropyWorkaround = + gl.ext.EXT_texture_filter_anisotropic && + gl.bugs.texture_filter_anisotropic_broken_on_sampler; + if (anisotropyWorkaround) { + desc.emplace(); + } else { + desc.emplace(); + } + } + break; + } + } +} + +void GLDescriptorSet::update(OpenGLContext&, + descriptor_binding_t binding, GLBufferObject* bo, size_t offset, size_t size) noexcept { + assert_invariant(binding < descriptors.size()); + std::visit([=](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v || std::is_same_v) { + assert_invariant(arg.target != 0); + arg.id = bo ? bo->gl.id : 0; + arg.offset = uint32_t(offset); + arg.size = uint32_t(size); + assert_invariant(arg.id || (!arg.size && !offset)); + } else if constexpr (std::is_same_v) { + arg.bo = bo; + arg.offset = uint32_t(offset); + } else { + // API usage error. User asked to update the wrong type of descriptor. + PANIC_PRECONDITION("descriptor %d is not a buffer", +binding); + } + }, descriptors[binding].desc); +} + +void GLDescriptorSet::update(OpenGLContext& gl, + descriptor_binding_t binding, GLTexture* t, SamplerParams params) noexcept { + assert_invariant(binding < descriptors.size()); + std::visit([=, &gl](auto&& arg) mutable { + using T = std::decay_t; + if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v) { + if (UTILS_UNLIKELY(t && t->target == SamplerType::SAMPLER_EXTERNAL)) { + // From OES_EGL_image_external spec: + // "The default s and t wrap modes are CLAMP_TO_EDGE, and it is an INVALID_ENUM + // error to set the wrap mode to any other value." + params.wrapS = SamplerWrapMode::CLAMP_TO_EDGE; + params.wrapT = SamplerWrapMode::CLAMP_TO_EDGE; + params.wrapR = SamplerWrapMode::CLAMP_TO_EDGE; + } + // GLES3.x specification forbids depth textures to be filtered. + if (t && isDepthFormat(t->format) + && params.compareMode == SamplerCompareMode::NONE) { + params.filterMag = SamplerMagFilter::NEAREST; + switch (params.filterMin) { + case SamplerMinFilter::LINEAR: + params.filterMin = SamplerMinFilter::NEAREST; + break; + case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: + case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: + case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: + params.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST; + break; + default: + break; + } + } + + arg.target = t ? t->gl.target : 0; + arg.id = t ? t->gl.id : 0; + arg.anisotropy = float(1u << params.anisotropyLog2); + if constexpr (std::is_same_v || + std::is_same_v) { +#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 + arg.sampler = gl.getSampler(params); +#else + (void)gl; +#endif + } else { + arg.params = params; + } + } else { + // API usage error. User asked to update the wrong type of descriptor. + PANIC_PRECONDITION("descriptor %d is not a texture", +binding); + } + }, descriptors[binding].desc); +} + +void GLDescriptorSet::bind(OpenGLContext& gl, OpenGLProgram const& p, descriptor_set_t set, + uint32_t const* offsets, bool offsetsOnly) const noexcept { + // TODO: check that offsets is sized correctly + size_t dynamicOffsetIndex = 0; + + utils::bitset64 activeDescriptorBindings = p.getActiveDescriptors(set); + if (offsetsOnly) { + activeDescriptorBindings &= dynamicBuffers; + } + + // loop only over the active indices for this program + activeDescriptorBindings.forEachSetBit([this, &gl, &p, set, offsets, &dynamicOffsetIndex] + (size_t binding) { + + // This would fail here if we're trying to set a descriptor that doesn't exist in the + // program. In other words, a mismatch between the program's layout and this descriptor-set. + assert_invariant(binding < descriptors.size()); + + auto const& entry = descriptors[binding]; + std::visit([&gl, &p, &dynamicOffsetIndex, set, binding, offsets](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + GLuint const bindingPoint = p.getBufferBinding(set, binding); + GLintptr const offset = arg.offset; + assert_invariant(arg.id || (!arg.size && !offset)); + gl.bindBufferRange(arg.target, bindingPoint, arg.id, offset, arg.size); + } else if constexpr (std::is_same_v) { + GLuint const bindingPoint = p.getBufferBinding(set, binding); + GLintptr const offset = arg.offset + offsets[dynamicOffsetIndex++]; + assert_invariant(arg.id || (!arg.size && !offset)); + gl.bindBufferRange(arg.target, bindingPoint, arg.id, offset, arg.size); + } else if constexpr (std::is_same_v) { + GLuint const bindingPoint = p.getBufferBinding(set, binding); + GLintptr offset = arg.offset; + if (arg.dynamicOffset) { + offset += offsets[dynamicOffsetIndex++]; + } + if (arg.bo) { + auto buffer = static_cast(arg.bo->gl.buffer) + offset; + p.updateUniforms(bindingPoint, arg.bo->gl.id, buffer, arg.bo->age); + } + } else if constexpr (std::is_same_v) { + GLuint const unit = p.getTextureUnit(set, binding); + if (arg.target) { + gl.bindTexture(unit, arg.target, arg.id); + gl.bindSampler(unit, arg.sampler); + } else { + gl.unbindTextureUnit(unit); + } + } else if constexpr (std::is_same_v) { + GLuint const unit = p.getTextureUnit(set, binding); + if (arg.target) { + gl.bindTexture(unit, arg.target, arg.id); + gl.bindSampler(unit, arg.sampler); +#if defined(GL_EXT_texture_filter_anisotropic) + // Driver claims to support anisotropic filtering, but it fails when set on + // the sampler, we have to set it on the texture instead. + glTexParameterf(arg.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, + std::min(gl.gets.max_anisotropy, arg.anisotropy)); +#endif + } else { + gl.unbindTextureUnit(unit); + } + } else if constexpr (std::is_same_v) { + // in ES2 the sampler parameters need to be set on the texture itself + GLuint const unit = p.getTextureUnit(set, binding); + if (arg.target) { + gl.bindTexture(unit, arg.target, arg.id); + SamplerParams const params = arg.params; + glTexParameteri(arg.target, GL_TEXTURE_MIN_FILTER, + (GLint)GLUtils::getTextureFilter(params.filterMin)); + glTexParameteri(arg.target, GL_TEXTURE_MAG_FILTER, + (GLint)GLUtils::getTextureFilter(params.filterMag)); + glTexParameteri(arg.target, GL_TEXTURE_WRAP_S, + (GLint)GLUtils::getWrapMode(params.wrapS)); + glTexParameteri(arg.target, GL_TEXTURE_WRAP_T, + (GLint)GLUtils::getWrapMode(params.wrapT)); +#if defined(GL_EXT_texture_filter_anisotropic) + glTexParameterf(arg.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, + std::min(gl.gets.max_anisotropy, arg.anisotropy)); +#endif + } else { + gl.unbindTextureUnit(unit); + } + } + }, entry.desc); + }); + CHECK_GL_ERROR(utils::slog.e) +} + +void GLDescriptorSet::validate(HandleAllocatorGL& allocator, + DescriptorSetLayoutHandle pipelineLayout) const { + + if (UTILS_UNLIKELY(dslh != pipelineLayout)) { + auto* const dsl = allocator.handle_cast < GLDescriptorSetLayout const * > (dslh); + auto* const cur = allocator.handle_cast < GLDescriptorSetLayout const * > (pipelineLayout); + + UTILS_UNUSED_IN_RELEASE + bool const pipelineLayoutMatchesDescriptorSetLayout = std::equal( + dsl->bindings.begin(), dsl->bindings.end(), + cur->bindings.begin(), + [](DescriptorSetLayoutBinding const& lhs, + DescriptorSetLayoutBinding const& rhs) { + return lhs.type == rhs.type && + lhs.stageFlags == rhs.stageFlags && + lhs.binding == rhs.binding && + lhs.flags == rhs.flags && + lhs.count == rhs.count; + }); + + assert_invariant(pipelineLayoutMatchesDescriptorSetLayout); + } +} + +} // namespace filament::backend diff --git a/filament/backend/src/opengl/GLDescriptorSet.h b/filament/backend/src/opengl/GLDescriptorSet.h new file mode 100644 index 00000000000..72d42fec33c --- /dev/null +++ b/filament/backend/src/opengl/GLDescriptorSet.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLDESCRIPTORSET_H +#define TNT_FILAMENT_BACKEND_OPENGL_GLDESCRIPTORSET_H + +#include "DriverBase.h" + +#include "gl_headers.h" + +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +namespace filament::backend { + +struct GLBufferObject; +struct GLTexture; +struct GLDescriptorSetLayout; +class OpenGLProgram; +class OpenGLContext; +class OpenGLDriver; + +struct GLDescriptorSet : public HwDescriptorSet { + + using HwDescriptorSet::HwDescriptorSet; + + GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh, + GLDescriptorSetLayout const* layout) noexcept; + + // update a buffer descriptor in the set + void update(OpenGLContext& gl, + descriptor_binding_t binding, GLBufferObject* bo, size_t offset, size_t size) noexcept; + + // update a sampler descriptor in the set + void update(OpenGLContext& gl, + descriptor_binding_t binding, GLTexture* t, SamplerParams params) noexcept; + + // conceptually bind the set to the command buffer + void bind(OpenGLContext& gl, OpenGLProgram const& p, + descriptor_set_t set, uint32_t const* offsets, bool offsetsOnly) const noexcept; + + uint32_t getDynamicBufferCount() const noexcept { + return dynamicBufferCount; + } + + void validate(HandleAllocatorGL& allocator, DescriptorSetLayoutHandle pipelineLayout) const; + +private: + // a Buffer Descriptor such as SSBO or UBO with static offset + struct Buffer { + Buffer() = default; + explicit Buffer(GLenum target) noexcept : target(target) { } + GLenum target; // 4 + GLuint id = 0; // 4 + uint32_t offset = 0; // 4 + uint32_t size = 0; // 4 + }; + + // a Buffer Descriptor such as SSBO or UBO with dynamic offset + struct DynamicBuffer { + DynamicBuffer() = default; + explicit DynamicBuffer(GLenum target) noexcept : target(target) { } + GLenum target; // 4 + GLuint id = 0; // 4 + uint32_t offset = 0; // 4 + uint32_t size = 0; // 4 + }; + + // a UBO descriptor for ES2 + struct BufferGLES2 { + BufferGLES2() = default; + explicit BufferGLES2(bool dynamicOffset) noexcept : dynamicOffset(dynamicOffset) { } + GLBufferObject const* bo = nullptr; // 8 + uint32_t offset = 0; // 4 + bool dynamicOffset = false; // 4 + }; + + // A sampler descriptor + struct Sampler { + GLenum target = 0; // 4 + GLuint id = 0; // 4 + GLuint sampler = 0; // 4 + float anisotropy = 1.0f; // 4 + }; + + struct SamplerWithAnisotropyWorkaround { + GLenum target = 0; // 4 + GLuint id = 0; // 4 + GLuint sampler = 0; // 4 + float anisotropy = 1.0f; // 4 + }; + + // A sampler descriptor for ES2 + struct SamplerGLES2 { + GLenum target = 0; // 4 + GLuint id = 0; // 4 + SamplerParams params{}; // 4 + float anisotropy = 1.0f; // 4 + }; + struct Descriptor { + std::variant< + Buffer, + DynamicBuffer, + BufferGLES2, + Sampler, + SamplerWithAnisotropyWorkaround, + SamplerGLES2> desc; + }; + static_assert(sizeof(Descriptor) <= 32); + + utils::FixedCapacityVector descriptors; // 16 + utils::bitset64 dynamicBuffers; // 8 + DescriptorSetLayoutHandle dslh; // 4 + uint8_t dynamicBufferCount = 0; // 1 +}; +static_assert(sizeof(GLDescriptorSet) == 32); + +} // namespace filament::backend + +#endif //TNT_FILAMENT_BACKEND_OPENGL_GLDESCRIPTORSET_H diff --git a/filament/backend/src/opengl/GLDescriptorSetLayout.h b/filament/backend/src/opengl/GLDescriptorSetLayout.h new file mode 100644 index 00000000000..bce3519fceb --- /dev/null +++ b/filament/backend/src/opengl/GLDescriptorSetLayout.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLDESCRIPTORSETLAYOUT_H +#define TNT_FILAMENT_BACKEND_OPENGL_GLDESCRIPTORSETLAYOUT_H + +#include "DriverBase.h" + +#include + +#include +#include + +#include + +namespace filament::backend { + +struct GLDescriptorSetLayout : public HwDescriptorSetLayout, public DescriptorSetLayout { + using HwDescriptorSetLayout::HwDescriptorSetLayout; + explicit GLDescriptorSetLayout(DescriptorSetLayout&& layout) noexcept + : DescriptorSetLayout(std::move(layout)) { + + std::sort(bindings.begin(), bindings.end(), + [](auto&& lhs, auto&& rhs){ + return lhs.binding < rhs.binding; + }); + + auto p = std::max_element(bindings.cbegin(), bindings.cend(), + [](auto const& lhs, auto const& rhs) { + return lhs.binding < rhs.binding; + }); + maxDescriptorBinding = p->binding; + } + uint8_t maxDescriptorBinding = 0; +}; + +} // namespace filament::backend + +#endif //TNT_FILAMENT_BACKEND_OPENGL_GLDESCRIPTORSETLAYOUT_H diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index 902fcc9f094..8659ba3d2af 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -60,10 +60,19 @@ class OpenGLContext final : public TimerQueryFactoryInterface { struct RenderPrimitive { static_assert(MAX_VERTEX_ATTRIBUTE_COUNT <= 16); - GLuint vao[2] = {}; // 4 + GLuint vao[2] = {}; // 8 GLuint elementArray = 0; // 4 + GLenum indicesType = 0; // 4 + + // The optional 32-bit handle to a GLVertexBuffer is necessary only if the referenced + // VertexBuffer supports buffer objects. If this is zero, then the VBO handles array is + // immutable. + Handle vertexBufferWithObjects; // 4 + mutable utils::bitset vertexAttribArray; // 2 + uint8_t reserved[2] = {}; // 2 + // if this differs from vertexBufferWithObjects->bufferObjectsVersion, this VAO needs to // be updated (see OpenGLDriver::updateVertexArrayObject()) uint8_t vertexBufferVersion = 0; // 1 @@ -76,16 +85,11 @@ class OpenGLContext final : public TimerQueryFactoryInterface { // See OpenGLContext::bindVertexArray() uint8_t nameVersion = 0; // 1 - // Size in bytes of indices in the index buffer - uint8_t indicesSize = 0; // 1 - - // The optional 32-bit handle to a GLVertexBuffer is necessary only if the referenced - // VertexBuffer supports buffer objects. If this is zero, then the VBO handles array is - // immutable. - Handle vertexBufferWithObjects; // 4 + // Size in bytes of indices in the index buffer (1 or 2) + uint8_t indicesShift = 0; // 1 GLenum getIndicesType() const noexcept { - return indicesSize == 4 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; + return indicesType; } } gl; @@ -473,12 +477,6 @@ class OpenGLContext final : public TimerQueryFactoryInterface { void unbindEverything() noexcept; void synchronizeStateAndCache(size_t index) noexcept; - void setEs2UniformBinding(size_t index, GLuint id, void const* data, uint16_t age) noexcept { - mUniformBindings[index] = { id, data, age }; - } - auto getEs2UniformBinding(size_t index) const noexcept { - return mUniformBindings[index]; - } #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 GLuint getSamplerSlow(SamplerParams sp) const noexcept; @@ -505,9 +503,6 @@ class OpenGLContext final : public TimerQueryFactoryInterface { std::vector> mDestroyWithNormalContext; RenderPrimitive mDefaultVAO; std::optional mDefaultFbo[2]; - std::array< - std::tuple, - CONFIG_UNIFORM_BINDING_COUNT> mUniformBindings = {}; mutable tsl::robin_map mSamplerMap; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 414d0c8aa21..83253c7f40c 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -28,19 +28,20 @@ #include #include +#include #include #include #include #include #include #include -#include #include #include "private/backend/Dispatcher.h" #include "private/backend/DriverApi.h" #include +#include #include #include #include @@ -59,7 +60,9 @@ #include #include #include +#include #include +#include #include #include @@ -124,17 +127,16 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform, // this is useful for development, but too verbose even for debug builds // For reference on a 64-bits machine in Release mode: // GLIndexBuffer : 8 moderate - // GLSamplerGroup : 16 few // GLSwapChain : 16 few // GLTimerQuery : 16 few // GLFence : 24 few // GLRenderPrimitive : 32 many // GLBufferObject : 32 many // -- less than or equal 32 bytes - // OpenGLProgram : 56 moderate // GLTexture : 64 moderate - // -- less than or equal 64 bytes // GLVertexBuffer : 76 moderate + // OpenGLProgram : 96 moderate + // -- less than or equal 96 bytes // GLStream : 104 few // GLRenderTarget : 112 few // GLVertexBufferInfo : 132 moderate @@ -146,7 +148,6 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform, << "\nGLVertexBuffer: " << sizeof(GLVertexBuffer) << "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo) << "\nGLIndexBuffer: " << sizeof(GLIndexBuffer) - << "\nGLSamplerGroup: " << sizeof(GLSamplerGroup) << "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive) << "\nGLTexture: " << sizeof(GLTexture) << "\nGLTimerQuery: " << sizeof(GLTimerQuery) @@ -215,8 +216,6 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi mDriverConfig(driverConfig), mCurrentPushConstants(new(std::nothrow) PushConstantBundle{}) { - std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr); - // set a reasonable default value for our stream array mTexturesWithStreamsAttached.reserve(8); mStreamsWithPendingAcquiredImage.reserve(8); @@ -345,17 +344,25 @@ bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept { return false; } - // set-up textures and samplers in the proper TMUs (as specified in setSamplers) + if (UTILS_UNLIKELY(mBoundProgram == p)) { + // program didn't change, don't do anything. + return true; + } + + // TODO: we could even improve this if the program could tell us which of the descriptors + // bindings actually changed. In practice, it is likely that set 0 or 1 might not + // change often. + decltype(mInvalidDescriptorSetBindings) changed; + changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1); + mInvalidDescriptorSetBindings |= changed; + + mBoundProgram = p; + + // compile/link the program if needed and call glUseProgram p->use(this, mContext); if (UTILS_UNLIKELY(mContext.isES2())) { - for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) { - auto [id, buffer, age] = mContext.getEs2UniformBinding(i); - if (buffer) { - p->updateUniforms(i, id, buffer, age); - } - } - // Set the output colorspace for this program (linear or rec709). This in only relevant + // Set the output colorspace for this program (linear or rec709). This is only relevant // when mPlatform.isSRGBSwapChainSupported() is false (no need to check though). p->setRec709ColorSpace(mRec709OutputColorspace); } @@ -487,7 +494,8 @@ Handle OpenGLDriver::createProgramS() noexcept { } Handle OpenGLDriver::createSamplerGroupS() noexcept { - return initHandle(); + // TODO: goes away + return {}; } Handle OpenGLDriver::createTextureS() noexcept { @@ -526,6 +534,14 @@ Handle OpenGLDriver::createTimerQueryS() noexcept { return initHandle(); } +Handle OpenGLDriver::createDescriptorSetLayoutS() noexcept { + return initHandle(); +} + +Handle OpenGLDriver::createDescriptorSetS() noexcept { + return initHandle(); +} + void OpenGLDriver::createVertexBufferInfoR( Handle vbih, uint8_t bufferCount, @@ -598,7 +614,8 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph, GLVertexBuffer* const vb = handle_cast(vbh); GLRenderPrimitive* const rp = handle_cast(rph); - rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u; + rp->gl.indicesShift = (ib->elementSize == 4u) ? 2u : 1u; + rp->gl.indicesType = (ib->elementSize == 4u) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; rp->gl.vertexBufferWithObjects = vbh; rp->type = pt; rp->vbih = vb->vbih; @@ -653,10 +670,8 @@ void OpenGLDriver::createProgramR(Handle ph, Program&& program) { } void OpenGLDriver::createSamplerGroupR(Handle sbh, uint32_t size, - utils::FixedSizeString<32> debugName) { - DEBUG_MARKER() - - construct(sbh, size); + utils::FixedSizeString<32>) { + // TODO: goes away } UTILS_NOINLINE @@ -1532,6 +1547,19 @@ void OpenGLDriver::createTimerQueryR(Handle tqh, int) { mContext.createTimerQuery(tq); } +void OpenGLDriver::createDescriptorSetLayoutR(Handle dslh, + DescriptorSetLayout&& info) { + DEBUG_MARKER() + construct(dslh, std::move(info)); +} + +void OpenGLDriver::createDescriptorSetR(Handle dsh, + Handle dslh) { + DEBUG_MARKER() + GLDescriptorSetLayout const* dsl = handle_cast(dslh); + construct(dsh, mContext, dslh, dsl); +} + // ------------------------------------------------------------------------------------------------ // Destroying driver objects // ------------------------------------------------------------------------------------------------ @@ -1610,16 +1638,7 @@ void OpenGLDriver::destroyProgram(Handle ph) { } void OpenGLDriver::destroySamplerGroup(Handle sbh) { - DEBUG_MARKER() - if (sbh) { - GLSamplerGroup* sb = handle_cast(sbh); - for (auto& binding : mSamplerBindings) { - if (binding == sb) { - binding = nullptr; - } - } - destruct(sbh, sb); - } + // TODO: goes away } void OpenGLDriver::destroyTexture(Handle th) { @@ -1742,6 +1761,28 @@ void OpenGLDriver::destroyTimerQuery(Handle tqh) { } } +void OpenGLDriver::destroyDescriptorSetLayout(Handle dslh) { + DEBUG_MARKER() + if (dslh) { + GLDescriptorSetLayout* dsl = handle_cast(dslh); + destruct(dslh, dsl); + } +} + +void OpenGLDriver::destroyDescriptorSet(Handle dsh) { + DEBUG_MARKER() + if (dsh) { + // unbind the descriptor-set, to avoid use-after-free + for (auto& bound : mBoundDescriptorSets) { + if (bound.dsh == dsh) { + bound = {}; + } + } + GLDescriptorSet* ds = handle_cast(dsh); + destruct(dsh, ds); + } +} + // ------------------------------------------------------------------------------------------------ // Synchronous APIs // These are called on the application's thread @@ -2310,89 +2351,8 @@ void OpenGLDriver::resetBufferObject(Handle boh) { } } -void OpenGLDriver::updateSamplerGroup(Handle sbh, - BufferDescriptor&& data) { - DEBUG_MARKER() - - OpenGLContext const& context = getContext(); - -#if defined(GL_EXT_texture_filter_anisotropic) - const bool anisotropyWorkaround = - context.ext.EXT_texture_filter_anisotropic && - context.bugs.texture_filter_anisotropic_broken_on_sampler; -#endif - - GLSamplerGroup* const sb = handle_cast(sbh); - assert_invariant(sb->textureUnitEntries.size() == data.size / sizeof(SamplerDescriptor)); - -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - bool const es2 = context.isES2(); -#endif - - auto const* const pSamplers = (SamplerDescriptor const*)data.buffer; - for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) { - GLuint samplerId = 0u; - const GLTexture* t = nullptr; - if (UTILS_LIKELY(pSamplers[i].t)) { - t = handle_cast(pSamplers[i].t); - assert_invariant(t); - - SamplerParams params = pSamplers[i].s; - if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) { - // From OES_EGL_image_external spec: - // "The default s and t wrap modes are CLAMP_TO_EDGE, and it is an INVALID_ENUM - // error to set the wrap mode to any other value." - params.wrapS = SamplerWrapMode::CLAMP_TO_EDGE; - params.wrapT = SamplerWrapMode::CLAMP_TO_EDGE; - params.wrapR = SamplerWrapMode::CLAMP_TO_EDGE; - } - // GLES3.x specification forbids depth textures to be filtered. - if (UTILS_UNLIKELY(isDepthFormat(t->format) - && params.compareMode == SamplerCompareMode::NONE - && params.filterMag != SamplerMagFilter::NEAREST - && params.filterMin != SamplerMinFilter::NEAREST - && params.filterMin != SamplerMinFilter::NEAREST_MIPMAP_NEAREST)) { - params.filterMag = SamplerMagFilter::NEAREST; - params.filterMin = SamplerMinFilter::NEAREST; -#ifndef NDEBUG - slog.w << "HwSamplerGroup specifies a filtered depth texture, which is not allowed." - << io::endl; -#endif - } -#if defined(GL_EXT_texture_filter_anisotropic) - if (UTILS_UNLIKELY(anisotropyWorkaround)) { - // Driver claims to support anisotropic filtering, but it fails when set on - // the sampler, we have to set it on the texture instead. - // The texture is already bound here. - GLfloat const anisotropy = float(1u << params.anisotropyLog2); - glTexParameterf(t->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, - std::min(context.gets.max_anisotropy, anisotropy)); - } -#endif -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - if (UTILS_LIKELY(!es2)) { - samplerId = mContext.getSampler(params); - } else -#endif - { - // in ES2 the sampler parameters need to be set on the texture itself - glTexParameteri(t->gl.target, GL_TEXTURE_MIN_FILTER, - (GLint)getTextureFilter(params.filterMin)); - glTexParameteri(t->gl.target, GL_TEXTURE_MAG_FILTER, - (GLint)getTextureFilter(params.filterMag)); - glTexParameteri(t->gl.target, GL_TEXTURE_WRAP_S, - (GLint)getWrapMode(params.wrapS)); - glTexParameteri(t->gl.target, GL_TEXTURE_WRAP_T, - (GLint)getWrapMode(params.wrapT)); - } - } else { - // this happens if the program doesn't use all samplers of a sampler group, - // which is not an error. - } - - sb->textureUnitEntries[i] = { t, samplerId }; - } - scheduleDestroy(std::move(data)); +void OpenGLDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { + // TODO: goes away } void OpenGLDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { @@ -3076,61 +3036,16 @@ void OpenGLDriver::setScissor(Viewport const& scissor) noexcept { // ------------------------------------------------------------------------------------------------ void OpenGLDriver::bindUniformBuffer(uint32_t index, Handle ubh) { - DEBUG_MARKER() - GLBufferObject* ub = handle_cast(ubh); - assert_invariant(ub->bindingType == BufferObjectBinding::UNIFORM); - bindBufferRange(BufferObjectBinding::UNIFORM, index, ubh, 0, ub->byteCount); + // TODO: goes away } void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index, Handle ubh, uint32_t offset, uint32_t size) { - DEBUG_MARKER() - auto& gl = mContext; - - assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE || - bindingType == BufferObjectBinding::UNIFORM); - - GLBufferObject* ub = handle_cast(ubh); - - assert_invariant(offset + size <= ub->byteCount); - - if (UTILS_UNLIKELY(ub->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) { - gl.setEs2UniformBinding(index, - ub->gl.id, - static_cast(ub->gl.buffer) + offset, - ub->age); - } else { - GLenum const target = GLUtils::getBufferBindingType(bindingType); - - assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE || - ub->gl.binding == target); - - gl.bindBufferRange(target, GLuint(index), ub->gl.id, offset, size); - } - - CHECK_GL_ERROR(utils::slog.e) -} - -void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) { - DEBUG_MARKER() - auto& gl = mContext; - - if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) { - gl.setEs2UniformBinding(index, 0, nullptr, 0); - return; - } - - GLenum const target = GLUtils::getBufferBindingType(bindingType); - gl.bindBufferRange(target, GLuint(index), 0, 0, 0); - CHECK_GL_ERROR(utils::slog.e) + // TODO: goes away } void OpenGLDriver::bindSamplers(uint32_t index, Handle sbh) { - DEBUG_MARKER() - assert_invariant(index < Program::SAMPLER_BINDING_COUNT); - GLSamplerGroup* sb = handle_cast(sbh); - mSamplerBindings[index] = sb; - CHECK_GL_ERROR(utils::slog.e) + // TODO: goes away } void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) { @@ -3498,6 +3413,26 @@ void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) { insertEventMarker("endFrame"); } +void OpenGLDriver::updateDescriptorSetBuffer( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::BufferObjectHandle boh, + uint32_t offset, uint32_t size) { + GLDescriptorSet* ds = handle_cast(dsh); + GLBufferObject* bo = boh ? handle_cast(boh) : nullptr; + ds->update(mContext, binding, bo, offset, size); +} + +void OpenGLDriver::updateDescriptorSetTexture( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::TextureHandle th, + SamplerParams params) { + GLDescriptorSet* ds = handle_cast(dsh); + GLTexture* t = th ? handle_cast(th) : nullptr; + ds->update(mContext, binding, t, params); +} + void OpenGLDriver::flush(int) { DEBUG_MARKER() auto& gl = mContext; @@ -3853,6 +3788,8 @@ void OpenGLDriver::bindPipeline(PipelineState const& state) { OpenGLProgram* const p = handle_cast(state.program); mValidProgram = useProgram(p); (*mCurrentPushConstants) = p->getPushConstants(); + mCurrentSetLayout = state.pipelineLayout.setLayout; + // TODO: we should validate that the pipeline layout matches the program's } void OpenGLDriver::bindRenderPrimitive(Handle rph) { @@ -3876,17 +3813,80 @@ void OpenGLDriver::bindRenderPrimitive(Handle rph) { mBoundRenderPrimitive = rp; } +void OpenGLDriver::bindDescriptorSet( + backend::DescriptorSetHandle dsh, + backend::descriptor_set_t set, + backend::DescriptorSetOffsetArray&& offsets) { + // handle_cast<> here also serves to validate the handle (it actually cannot return nullptr) + GLDescriptorSet const* const ds = handle_cast(dsh); + if (ds) { + assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT); + if (mBoundDescriptorSets[set].dsh != dsh) { + // if the descriptor itself changed, we mark this descriptor binding + // invalid -- it will be re-bound at the next draw. + mInvalidDescriptorSetBindings.set(set, true); + } else if (!offsets.empty()) { + // if we reset offsets, we mark the offsets invalid so these descriptors only can + // be re-bound at the next draw. + mInvalidDescriptorSetBindingOffsets.set(set, true); + } + + // `offsets` data's lifetime will end when this function returns. We have to make a copy. + // (the data is allocated inside the CommandStream) + mBoundDescriptorSets[set].dsh = dsh; + std::copy_n(offsets.data(), ds->getDynamicBufferCount(), + mBoundDescriptorSets[set].offsets.data()); + } +} + +void OpenGLDriver::updateDescriptors(utils::bitset8 invalidDescriptorSets) noexcept { + assert_invariant(mBoundProgram); + auto const offsetOnly = mInvalidDescriptorSetBindingOffsets & ~mInvalidDescriptorSetBindings; + invalidDescriptorSets.forEachSetBit([this, offsetOnly, + &boundDescriptorSets = mBoundDescriptorSets, + &context = mContext, + &boundProgram = *mBoundProgram](size_t set) { + assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT); + auto const& entry = boundDescriptorSets[set]; + if (entry.dsh) { + GLDescriptorSet* const ds = handle_cast(entry.dsh); +#ifndef NDEBUG + if (UTILS_UNLIKELY(!offsetOnly[set])) { + // validate that this descriptor-set layout matches the layout set in the pipeline + // we don't need to do the check if only the offset is changing + ds->validate(mHandleAllocator, mCurrentSetLayout[set]); + } +#endif + ds->bind(context, boundProgram, set, entry.offsets.data(), offsetOnly[set]); + } + }); + mInvalidDescriptorSetBindings.clear(); + mInvalidDescriptorSetBindingOffsets.clear(); +} + void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { - GLRenderPrimitive const* const rp = mBoundRenderPrimitive; - if (UTILS_UNLIKELY(!rp || !mValidProgram)) { + assert_invariant(!mContext.isES2()); + assert_invariant(mBoundRenderPrimitive); +#if FILAMENT_ENABLE_MATDBG + if (UTILS_UNLIKELY(!mValidProgram)) { return; } +#endif + assert_invariant(mBoundProgram); + assert_invariant(mValidProgram); + + // When the program changes, we might have to rebind all or some descriptors + auto const invalidDescriptorSets = + mInvalidDescriptorSetBindings | mInvalidDescriptorSetBindingOffsets; + if (UTILS_UNLIKELY(invalidDescriptorSets.any())) { + updateDescriptors(invalidDescriptorSets); + } #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - assert_invariant(!mContext.isES2()); + GLRenderPrimitive const* const rp = mBoundRenderPrimitive; glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), - reinterpret_cast(indexOffset * rp->gl.indicesSize), + reinterpret_cast(indexOffset << rp->gl.indicesShift), (GLsizei)instanceCount); #endif @@ -3897,18 +3897,29 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins #endif } +// This is the ES2 version of draw2(). void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { - GLRenderPrimitive const* const rp = mBoundRenderPrimitive; - if (UTILS_UNLIKELY(!rp || !mValidProgram)) { + assert_invariant(mContext.isES2()); + assert_invariant(mBoundRenderPrimitive); +#if FILAMENT_ENABLE_MATDBG + if (UTILS_UNLIKELY(!mValidProgram)) { return; } +#endif + assert_invariant(mBoundProgram); + assert_invariant(mValidProgram); - assert_invariant(mContext.isES2()); - assert_invariant(instanceCount == 1); + // When the program changes, we might have to rebind all or some descriptors + auto const invalidDescriptorSets = + mInvalidDescriptorSetBindings | mInvalidDescriptorSetBindingOffsets; + if (UTILS_UNLIKELY(invalidDescriptorSets.any())) { + updateDescriptors(invalidDescriptorSets); + } + GLRenderPrimitive const* const rp = mBoundRenderPrimitive; + assert_invariant(instanceCount == 1); glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(), - reinterpret_cast(indexOffset * rp->gl.indicesSize)); - + reinterpret_cast(indexOffset << rp->gl.indicesShift)); #if FILAMENT_ENABLE_MATDBG CHECK_GL_ERROR_NON_FATAL(utils::slog.e) diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index d5e1b797b68..927ae925a58 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -21,6 +21,8 @@ #include "OpenGLContext.h" #include "OpenGLTimerQuery.h" #include "GLBufferObject.h" +#include "GLDescriptorSet.h" +#include "GLDescriptorSetLayout.h" #include "GLTexture.h" #include "ShaderCompilerService.h" @@ -36,6 +38,7 @@ #include "private/backend/Driver.h" #include "private/backend/HandleAllocator.h" +#include #include #include #include @@ -52,6 +55,7 @@ #include #include #include +#include #include #include @@ -123,16 +127,6 @@ class OpenGLDriver final : public DriverBase { } gl; }; - struct GLSamplerGroup : public HwSamplerGroup { - using HwSamplerGroup::HwSamplerGroup; - struct Entry { - GLTexture const* texture = nullptr; - GLuint sampler = 0u; - }; - utils::FixedCapacityVector textureUnitEntries; - explicit GLSamplerGroup(size_t size) noexcept : textureUnitEntries(size) { } - }; - struct GLRenderPrimitive : public HwRenderPrimitive { using HwRenderPrimitive::HwRenderPrimitive; OpenGLContext::RenderPrimitive gl; @@ -145,6 +139,10 @@ class OpenGLDriver final : public DriverBase { using GLTimerQuery = filament::backend::GLTimerQuery; + using GLDescriptorSetLayout = filament::backend::GLDescriptorSetLayout; + + using GLDescriptorSet = filament::backend::GLDescriptorSet; + struct GLStream : public HwStream { using HwStream::HwStream; struct Info { @@ -312,10 +310,6 @@ class OpenGLDriver final : public DriverBase { void resolvePass(ResolveAction action, GLRenderTarget const* rt, TargetBufferFlags discardFlags) noexcept; - const std::array& getSamplerBindings() const { - return mSamplerBindings; - } - using AttachmentArray = std::array; static GLsizei getAttachments(AttachmentArray& attachments, TargetBufferFlags buffers, bool isDefaultFramebuffer) noexcept; @@ -328,8 +322,16 @@ class OpenGLDriver final : public DriverBase { GLboolean mRenderPassStencilWrite{}; GLRenderPrimitive const* mBoundRenderPrimitive = nullptr; + OpenGLProgram* mBoundProgram = nullptr; bool mValidProgram = false; + utils::bitset8 mInvalidDescriptorSetBindings; + utils::bitset8 mInvalidDescriptorSetBindingOffsets; + void updateDescriptors(utils::bitset8 invalidDescriptorSets) noexcept; + struct { + backend::DescriptorSetHandle dsh; + std::array offsets; + } mBoundDescriptorSets[MAX_DESCRIPTOR_SET_COUNT]; void clearWithRasterPipe(TargetBufferFlags clearFlags, math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept; @@ -341,9 +343,6 @@ class OpenGLDriver final : public DriverBase { // ES2 only. Uniform buffer emulation binding points GLuint mLastAssignedEmulatedUboId = 0; - // sampler buffer binding points (nullptr if not used) - std::array mSamplerBindings = {}; // 4 pointers - // this must be accessed from the driver thread only std::vector mTexturesWithStreamsAttached; @@ -379,6 +378,7 @@ class OpenGLDriver final : public DriverBase { bool mRec709OutputColorspace = false; PushConstantBundle* mCurrentPushConstants = nullptr; + PipelineLayout::SetLayout mCurrentSetLayout; }; // ------------------------------------------------------------------------------------------------ diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 6c5eab9a442..42943075ee0 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -20,21 +20,23 @@ #include "OpenGLDriver.h" #include "ShaderCompilerService.h" +#include #include -#include - #include #include +#include #include #include #include +#include +#include #include #include -#include #include +#include namespace filament::backend { @@ -43,9 +45,8 @@ using namespace utils; using namespace backend; struct OpenGLProgram::LazyInitializationData { - Program::UniformBlockInfo uniformBlockInfo; - Program::SamplerGroupInfo samplerGroupInfo; - std::array bindingUniformInfo; + Program::DescriptorSetInfo descriptorBindings; + Program::BindingUniformsInfo bindingUniformInfo; utils::FixedCapacityVector vertexPushConstants; utils::FixedCapacityVector fragmentPushConstants; }; @@ -54,16 +55,14 @@ struct OpenGLProgram::LazyInitializationData { OpenGLProgram::OpenGLProgram() noexcept = default; OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& program) noexcept - : HwProgram(std::move(program.getName())) { + : HwProgram(std::move(program.getName())), mRec709Location(-1) { auto* const lazyInitializationData = new(std::nothrow) LazyInitializationData(); - lazyInitializationData->samplerGroupInfo = std::move(program.getSamplerGroupInfo()); if (UTILS_UNLIKELY(gld.getContext().isES2())) { lazyInitializationData->bindingUniformInfo = std::move(program.getBindingUniformInfo()); - } else { - lazyInitializationData->uniformBlockInfo = std::move(program.getUniformBlockBindings()); } lazyInitializationData->vertexPushConstants = std::move(program.getPushConstants(ShaderStage::VERTEX)); lazyInitializationData->fragmentPushConstants = std::move(program.getPushConstants(ShaderStage::FRAGMENT)); + lazyInitializationData->descriptorBindings = std::move(program.getDescriptorBindings()); ShaderCompilerService& compiler = gld.getShaderCompilerService(); mToken = compiler.createProgram(name, std::move(program)); @@ -121,36 +120,84 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra SYSTRACE_CALL(); + // from the pipeline layout we compute a mapping from {set, binding} to {binding} + // for both buffers and textures + + for (auto&& entry: lazyInitializationData.descriptorBindings) { + std::sort(entry.begin(), entry.end(), + [](Program::Descriptor const& lhs, Program::Descriptor const& rhs) { + return lhs.binding < rhs.binding; + }); + } + + GLuint tmu = 0; + GLuint binding = 0; + + // needed for samplers + context.useProgram(program); + + UTILS_NOUNROLL + for (backend::descriptor_set_t set = 0; set < MAX_DESCRIPTOR_SET_COUNT; set++) { + for (Program::Descriptor const& entry: lazyInitializationData.descriptorBindings[set]) { + switch (entry.type) { + case DescriptorType::UNIFORM_BUFFER: + case DescriptorType::SHADER_STORAGE_BUFFER: { + if (!entry.name.empty()) { #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - if (!context.isES2()) { - // Note: This is only needed, because the layout(binding=) syntax is not permitted in glsl - // (ES3.0 and GL4.1). The backend needs a way to associate a uniform block to a binding point. - UTILS_NOUNROLL - for (GLuint binding = 0, n = lazyInitializationData.uniformBlockInfo.size(); - binding < n; binding++) { - auto const& name = lazyInitializationData.uniformBlockInfo[binding]; - if (!name.empty()) { - GLuint const index = glGetUniformBlockIndex(program, name.c_str()); - if (index != GL_INVALID_INDEX) { - glUniformBlockBinding(program, index, binding); + if (UTILS_LIKELY(!context.isES2())) { + GLuint const index = glGetUniformBlockIndex(program, + entry.name.c_str()); + if (index != GL_INVALID_INDEX) { + // this can fail if the program doesn't use this descriptor + glUniformBlockBinding(program, index, binding); + mBindingMap.insert(set, entry.binding, + { binding, entry.type }); + ++binding; + } + } else +#endif + { + auto pos = std::find_if(lazyInitializationData.bindingUniformInfo.begin(), + lazyInitializationData.bindingUniformInfo.end(), + [&name = entry.name](const auto& item) { + return std::get<1>(item) == name; + }); + if (pos != lazyInitializationData.bindingUniformInfo.end()) { + binding = std::get<0>(*pos); + mBindingMap.insert(set, entry.binding, { binding, entry.type }); + } + } + } + break; + } + case DescriptorType::SAMPLER: { + if (!entry.name.empty()) { + GLint const loc = glGetUniformLocation(program, entry.name.c_str()); + if (loc >= 0) { + // this can fail if the program doesn't use this descriptor + mBindingMap.insert(set, entry.binding, { tmu, entry.type }); + glUniform1i(loc, GLint(tmu)); + ++tmu; + } + } + break; } - CHECK_GL_ERROR(utils::slog.e) } + CHECK_GL_ERROR(utils::slog.e) } - } else -#endif - { + } + + if (context.isES2()) { // ES2 initialization of (fake) UBOs UniformsRecord* const uniformsRecords = new(std::nothrow) UniformsRecord[Program::UNIFORM_BINDING_COUNT]; UTILS_NOUNROLL - for (GLuint binding = 0, n = Program::UNIFORM_BINDING_COUNT; binding < n; binding++) { - Program::UniformInfo& uniforms = lazyInitializationData.bindingUniformInfo[binding]; - uniformsRecords[binding].locations.reserve(uniforms.size()); - uniformsRecords[binding].locations.resize(uniforms.size()); + for (auto&& [index, name, uniforms] : lazyInitializationData.bindingUniformInfo) { + uniformsRecords[index].locations.reserve(uniforms.size()); + uniformsRecords[index].locations.resize(uniforms.size()); for (size_t j = 0, c = uniforms.size(); j < c; j++) { GLint const loc = glGetUniformLocation(program, uniforms[j].name.c_str()); - uniformsRecords[binding].locations[j] = loc; - if (UTILS_UNLIKELY(binding == 0)) { + uniformsRecords[index].locations[j] = loc; + if (UTILS_UNLIKELY(index == 0)) { // This is a bit of a gross hack here, we stash the location of // "frameUniforms.rec709", which obviously the backend shouldn't know about, // which is used for emulating the "rec709" colorspace in the shader. @@ -162,51 +209,11 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra } } } - uniformsRecords[binding].uniforms = std::move(uniforms); + uniformsRecords[index].uniforms = std::move(uniforms); } mUniformsRecords = uniformsRecords; } - uint8_t usedBindingCount = 0; - uint8_t tmu = 0; - - UTILS_NOUNROLL - for (size_t i = 0, c = lazyInitializationData.samplerGroupInfo.size(); i < c; i++) { - auto const& samplers = lazyInitializationData.samplerGroupInfo[i].samplers; - if (samplers.empty()) { - // this binding point doesn't have any samplers, skip it. - continue; - } - - // keep this in the loop, so we skip it in the rare case a program doesn't have - // sampler. The context cache will prevent repeated calls to GL. - context.useProgram(program); - - bool atLeastOneSamplerUsed = false; - UTILS_NOUNROLL - for (const Program::Sampler& sampler: samplers) { - // find its location and associate a TMU to it - GLint const loc = glGetUniformLocation(program, sampler.name.c_str()); - if (loc >= 0) { - // this can fail if the program doesn't use this sampler - glUniform1i(loc, tmu); - atLeastOneSamplerUsed = true; - } - tmu++; - } - - // if this program doesn't use any sampler from this HwSamplerGroup, just cancel the - // whole group. - if (atLeastOneSamplerUsed) { - // Cache the sampler uniform locations for each interface block - mUsedSamplerBindingPoints[usedBindingCount] = i; - usedBindingCount++; - } else { - tmu -= samplers.size(); - } - } - mUsedBindingsCount = usedBindingCount; - auto& vertexConstants = lazyInitializationData.vertexPushConstants; auto& fragmentConstants = lazyInitializationData.fragmentPushConstants; @@ -223,40 +230,8 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra } } -void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept { - using GLTexture = OpenGLDriver::GLTexture; - -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - bool const es2 = gld->getContext().isES2(); -#endif - - // cache a few member variable locally, outside the loop - auto const& UTILS_RESTRICT samplerBindings = gld->getSamplerBindings(); - auto const& UTILS_RESTRICT usedBindingPoints = mUsedSamplerBindingPoints; - - for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) { - size_t const binding = usedBindingPoints[i]; - assert_invariant(binding < Program::SAMPLER_BINDING_COUNT); - auto const * const sb = samplerBindings[binding]; - assert_invariant(sb); - if (!sb) continue; // should never happen, this would be a user error. - for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here - const GLTexture* const t = sb->textureUnitEntries[j].texture; - if (t) { // program may not use all samplers of sampler group - gld->bindTexture(tmu, t); -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - if (UTILS_LIKELY(!es2)) { - GLuint const s = sb->textureUnitEntries[j].sampler; - gld->bindSampler(tmu, s); - } -#endif - } - } - } - CHECK_GL_ERROR(utils::slog.e) -} - -void OpenGLProgram::updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept { +void OpenGLProgram::updateUniforms( + uint32_t index, GLuint id, void const* buffer, uint16_t age) const noexcept { assert_invariant(mUniformsRecords); assert_invariant(buffer); diff --git a/filament/backend/src/opengl/OpenGLProgram.h b/filament/backend/src/opengl/OpenGLProgram.h index 19be485ac6b..59cccf57f65 100644 --- a/filament/backend/src/opengl/OpenGLProgram.h +++ b/filament/backend/src/opengl/OpenGLProgram.h @@ -19,17 +19,20 @@ #include "DriverBase.h" +#include "BindingMap.h" #include "OpenGLContext.h" #include "ShaderCompilerService.h" #include + +#include #include +#include #include #include #include -#include #include #include @@ -57,33 +60,25 @@ class OpenGLProgram : public HwProgram { if (UTILS_UNLIKELY(!gl.program)) { initialize(*gld); } - context.useProgram(gl.program); - if (UTILS_UNLIKELY(mUsedBindingsCount)) { - // We rely on GL state tracking to avoid unnecessary glBindTexture / glBindSampler - // calls. + } - // we need to do this if: - // - the content of mSamplerBindings has changed - // - the content of any bound sampler buffer has changed - // ... since last time we used this program + GLuint getBufferBinding(descriptor_set_t set, descriptor_binding_t binding) const noexcept { + return mBindingMap.get(set, binding); + } - // Turns out the former might be relatively cheap to check, the latter requires - // a bit less. Compared to what updateSamplers() actually does, which is - // pretty little, I'm not sure if we'll get ahead. + GLuint getTextureUnit(descriptor_set_t set, descriptor_binding_t binding) const noexcept { + return mBindingMap.get(set, binding); + } - updateSamplers(gld); - } + utils::bitset64 getActiveDescriptors(descriptor_set_t set) const noexcept { + return mBindingMap.getActiveDescriptors(set); } // For ES2 only - void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept; + void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) const noexcept; void setRec709ColorSpace(bool rec709) const noexcept; - struct { - GLuint program = 0; - } gl; // 4 bytes - PushConstantBundle getPushConstants() { auto fragBegin = mPushConstants.begin() + mPushConstantFragmentStageOffset; return { @@ -101,22 +96,15 @@ class OpenGLProgram : public HwProgram { void initializeProgramState(OpenGLContext& context, GLuint program, LazyInitializationData& lazyInitializationData) noexcept; - void updateSamplers(OpenGLDriver* gld) const noexcept; - - // number of bindings actually used by this program - std::array mUsedSamplerBindingPoints; // 4 bytes + BindingMap mBindingMap; // 8 bytes + out-of-line 256 bytes ShaderCompilerService::program_token_t mToken{}; // 16 bytes - uint8_t mUsedBindingsCount = 0u; // 1 byte - UTILS_UNUSED uint8_t padding[2] = {}; // 2 byte - - // Push constant array offset for fragment stage constants. - uint8_t mPushConstantFragmentStageOffset = 0u; // 1 byte + // Note that this can be replaced with a raw pointer and an uint8_t (for size) to reduce the + // size of the container to 9 bytes if there is a need in the future. + utils::FixedCapacityVector> mPushConstants;// 16 bytes // only needed for ES2 - GLint mRec709Location = -1; // 4 bytes - using LocationInfo = utils::FixedCapacityVector; struct UniformsRecord { Program::UniformInfo uniforms; @@ -124,15 +112,20 @@ class OpenGLProgram : public HwProgram { mutable GLuint id = 0; mutable uint16_t age = std::numeric_limits::max(); }; - UniformsRecord const* mUniformsRecords = nullptr; // 8 bytes + UniformsRecord const* mUniformsRecords = nullptr; + GLint mRec709Location : 24; // 4 bytes - // Note that this can be replaced with a raw pointer and an uint8_t (for size) to reduce the - // size of the container to 9 bytes if there is a need in the future. - utils::FixedCapacityVector> mPushConstants;// 16 bytes + // Push constant array offset for fragment stage constants. + GLint mPushConstantFragmentStageOffset : 8; // 1 byte + +public: + struct { + GLuint program = 0; + } gl; // 4 bytes }; -// if OpenGLProgram is larger tha 64 bytes, it'll fall in a larger Handle bucket. -static_assert(sizeof(OpenGLProgram) <= 64); // currently 64 bytes +// if OpenGLProgram is larger than 96 bytes, it'll fall in a larger Handle bucket. +static_assert(sizeof(OpenGLProgram) <= 96); // currently 96 bytes } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 22e4e1814da..0057b89d314 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -422,6 +422,21 @@ void VulkanDriver::endFrame(uint32_t frameId) { FVK_SYSTRACE_END(); } +void VulkanDriver::updateDescriptorSetBuffer( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::BufferObjectHandle boh, + uint32_t offset, + uint32_t size) { +} + +void VulkanDriver::updateDescriptorSetTexture( + backend::DescriptorSetHandle dsh, + backend::descriptor_binding_t binding, + backend::TextureHandle th, + SamplerParams params) { +} + void VulkanDriver::flush(int) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("flush"); @@ -686,6 +701,14 @@ void VulkanDriver::createTimerQueryR(Handle tqh, int) { // nothing to do, timer query was constructed in createTimerQueryS } +void VulkanDriver::createDescriptorSetLayoutR(Handle dslh, + backend::DescriptorSetLayout&& info) { +} + +void VulkanDriver::createDescriptorSetR(Handle dsh, + Handle dslh) { +} + Handle VulkanDriver::createVertexBufferInfoS() noexcept { return mResourceAllocator.allocHandle(); } @@ -756,6 +779,14 @@ Handle VulkanDriver::createTimerQueryS() noexcept { return tqh; } +Handle VulkanDriver::createDescriptorSetLayoutS() noexcept { + return mResourceAllocator.allocHandle(); +} + +Handle VulkanDriver::createDescriptorSetS() noexcept { + return mResourceAllocator.allocHandle(); +} + void VulkanDriver::destroySamplerGroup(Handle sbh) { if (!sbh) { return; @@ -795,6 +826,12 @@ void VulkanDriver::destroyTimerQuery(Handle tqh) { mThreadSafeResourceManager.release(vtq); } +void VulkanDriver::destroyDescriptorSetLayout(Handle dslh) { +} + +void VulkanDriver::destroyDescriptorSet(Handle dsh) { +} + Handle VulkanDriver::createStreamNative(void* nativeStream) { return {}; } @@ -1575,10 +1612,6 @@ void VulkanDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind mDescriptorSetManager.updateBuffer({}, (uint32_t) index, bo, offset, size); } -void VulkanDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) { - mDescriptorSetManager.clearBuffer((uint32_t) index); -} - void VulkanDriver::bindSamplers(uint32_t index, Handle sbh) { auto* hwsb = mResourceAllocator.handle_cast(sbh); mSamplerBindings[index] = hwsb; @@ -1923,6 +1956,12 @@ void VulkanDriver::bindRenderPrimitive(Handle rph) { FVK_SYSTRACE_END(); } +void VulkanDriver::bindDescriptorSet( + backend::DescriptorSetHandle dsh, + backend::descriptor_set_t set, + backend::DescriptorSetOffsetArray&& offsets) { +} + void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { FVK_SYSTRACE_CONTEXT(); FVK_SYSTRACE_START("draw2"); diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index ff3f137c108..3cd2ff38fc6 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -72,7 +72,7 @@ using LayoutDescriptionList = VulkanProgram::LayoutDescriptionList; template void addDescriptors(Bitmask mask, - utils::FixedCapacityVector& outputList) { + utils::FixedCapacityVector& outputList) { constexpr uint8_t MODULE_OFFSET = (sizeof(Bitmask) * 8) / MAX_SHADER_MODULES; for (uint8_t i = 0; i < MODULE_OFFSET; ++i) { bool const hasVertex = (mask & (1ULL << i)) != 0; @@ -81,9 +81,9 @@ void addDescriptors(Bitmask mask, continue; } - DescriptorSetLayoutBinding binding{ + descset::DescriptorSetLayoutBinding binding{ .binding = i, - .flags = DescriptorFlags::NONE, + .flags = descset::DescriptorFlags::NONE, .count = 0,// This is always 0 for now as we pass the size of the UBOs in the Driver API // instead. }; @@ -95,11 +95,11 @@ void addDescriptors(Bitmask mask, binding.stageFlags | ShaderStageFlags2::FRAGMENT); } if constexpr (std::is_same_v) { - binding.type = DescriptorType::UNIFORM_BUFFER; + binding.type = descset::DescriptorType::UNIFORM_BUFFER; } else if constexpr (std::is_same_v) { - binding.type = DescriptorType::SAMPLER; + binding.type = descset::DescriptorType::SAMPLER; } else if constexpr (std::is_same_v) { - binding.type = DescriptorType::INPUT_ATTACHMENT; + binding.type = descset::DescriptorType::INPUT_ATTACHMENT; } outputList.push_back(binding); } @@ -258,11 +258,11 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept } LayoutDescriptionList& layouts = mInfo->layouts; - layouts[0].bindings = utils::FixedCapacityVector::with_capacity( + layouts[0].bindings = utils::FixedCapacityVector::with_capacity( countBits(collapseStages(uboMask))); - layouts[1].bindings = utils::FixedCapacityVector::with_capacity( + layouts[1].bindings = utils::FixedCapacityVector::with_capacity( countBits(collapseStages(samplerMask))); - layouts[2].bindings = utils::FixedCapacityVector::with_capacity( + layouts[2].bindings = utils::FixedCapacityVector::with_capacity( countBits(collapseStages(inputAttachmentMask))); addDescriptors(uboMask, layouts[0].bindings); @@ -273,23 +273,23 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept auto& bindingToName = mInfo->bindingToName; #endif - auto& groupInfo = builder.getSamplerGroupInfo(); - auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; - auto& bindings = mInfo->bindings; - for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) { - auto const& group = groupInfo[groupInd]; - auto const& samplers = group.samplers; - for (size_t i = 0; i < samplers.size(); ++i) { - uint32_t const binding = samplers[i].binding; - bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i); - assert_invariant(bindings.find(binding) == bindings.end()); - bindings.insert(binding); - -#if FVK_ENABLED_DEBUG_SAMPLER_NAME - bindingToName[binding] = samplers[i].name.c_str(); -#endif - } - } +// backend::SamplerGroupInfo& groupInfo = builder.getSamplerGroupInfo(); +// auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; +// auto& bindings = mInfo->bindings; +// for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) { +// auto const& group = groupInfo[groupInd]; +// auto const& samplers = group.samplers; +// for (size_t i = 0; i < samplers.size(); ++i) { +// uint32_t const binding = samplers[i].binding; +// bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i); +// assert_invariant(bindings.find(binding) == bindings.end()); +// bindings.insert(binding); +// +//#if FVK_ENABLED_DEBUG_SAMPLER_NAME +// bindingToName[binding] = samplers[i].name.c_str(); +//#endif +// } +// } #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0] diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 75596ea93cd..52032a16a19 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -482,6 +482,12 @@ struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource { utils::Mutex mFenceMutex; }; +struct VulkanDescriptorSetLayout2 : public HwDescriptorSetLayout, VulkanResource { +}; + +struct VulkanDescriptorSet2 : public HwDescriptorSet, VulkanResource { +}; + inline constexpr VkBufferUsageFlagBits getBufferObjectUsage( BufferObjectBinding bindingType) noexcept { switch(bindingType) { diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index 5bb694c974b..6eede04f00c 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -112,9 +112,10 @@ void BackendTest::fullViewport(Viewport& viewport) { } void BackendTest::renderTriangle( - filament::backend::Handle renderTarget, - filament::backend::Handle swapChain, - filament::backend::Handle program) { + PipelineLayout const& pipelineLayout, + Handle renderTarget, + Handle swapChain, + Handle program) { RenderPassParams params = {}; fullViewport(params); params.flags.clear = TargetBufferFlags::COLOR; @@ -123,11 +124,15 @@ void BackendTest::renderTriangle( params.flags.discardEnd = TargetBufferFlags::NONE; params.viewport.height = 512; params.viewport.width = 512; - renderTriangle(renderTarget, swapChain, program, params); + renderTriangle(pipelineLayout, renderTarget, swapChain, program, params); } -void BackendTest::renderTriangle(Handle renderTarget, - Handle swapChain, Handle program, const RenderPassParams& params) { +void BackendTest::renderTriangle( + PipelineLayout const& pipelineLayout, + Handle renderTarget, + Handle swapChain, + Handle program, + const RenderPassParams& params) { auto& api = getDriverApi(); TrianglePrimitive triangle(api); @@ -138,6 +143,7 @@ void BackendTest::renderTriangle(Handle renderTarget, PipelineState state; state.program = program; + state.pipelineLayout = pipelineLayout; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; diff --git a/filament/backend/test/BackendTest.h b/filament/backend/test/BackendTest.h index 777a96f0404..981b0eaa074 100644 --- a/filament/backend/test/BackendTest.h +++ b/filament/backend/test/BackendTest.h @@ -51,10 +51,15 @@ class BackendTest : public ::testing::Test { static void fullViewport(filament::backend::RenderPassParams& params); static void fullViewport(filament::backend::Viewport& viewport); - void renderTriangle(filament::backend::Handle renderTarget, + void renderTriangle( + filament::backend::PipelineLayout const& pipelineLayout, + filament::backend::Handle renderTarget, filament::backend::Handle swapChain, filament::backend::Handle program); - void renderTriangle(filament::backend::Handle renderTarget, + + void renderTriangle( + filament::backend::PipelineLayout const& pipelineLayout, + filament::backend::Handle renderTarget, filament::backend::Handle swapChain, filament::backend::Handle program, const filament::backend::RenderPassParams& params); diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index 61c8b89e5e3..599ac9e71e4 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -350,10 +350,19 @@ TEST_F(BackendTest, ColorResolve) { { ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform); Program prog = shaderGen.getProgram(api); - prog.uniformBlockBindings({{"params", 1}}); + prog.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); program = api.createProgram(std::move(prog)); } + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + // Create a VertexBuffer, IndexBuffer, and RenderPrimitive. TrianglePrimitive const triangle(api); @@ -388,6 +397,7 @@ TEST_F(BackendTest, ColorResolve) { PipelineState state = {}; state.program = program; + state.pipelineLayout.setLayout = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; @@ -401,10 +411,12 @@ TEST_F(BackendTest, ColorResolve) { .scale = float4(1, 1, 0.5, 0), }); + api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams)); + api.bindDescriptorSet(descriptorSet, 0, {}); + // FIXME: on Metal this triangle is not drawn. Can't understand why. api.beginFrame(0, 0, 0); api.beginRenderPass(srcRenderTarget, params); - api.bindUniformBuffer(0, ubuffer); api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); api.endFrame(0); @@ -428,6 +440,8 @@ TEST_F(BackendTest, ColorResolve) { EXPECT_TRUE(sparams.pixelHashResult == expected); // Cleanup. + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyBufferObject(ubuffer); api.destroyProgram(program); api.destroyTexture(srcColorTexture); diff --git a/filament/backend/test/test_BufferUpdates.cpp b/filament/backend/test/test_BufferUpdates.cpp index bde771448a1..e93b088116a 100644 --- a/filament/backend/test/test_BufferUpdates.cpp +++ b/filament/backend/test/test_BufferUpdates.cpp @@ -89,20 +89,32 @@ TEST_F(BackendTest, VertexBufferUpdate) { // The test is executed within this block scope to force destructors to run before // executeCommands(). { + auto& api = getDriverApi(); + // Create a platform-specific SwapChain and make it current. auto swapChain = createSwapChain(); - getDriverApi().makeCurrent(swapChain, swapChain); + api.makeCurrent(swapChain, swapChain); // Create a program. ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform); - Program p = shaderGen.getProgram(getDriverApi()); - auto program = getDriverApi().createProgram(std::move(p)); + Program p = shaderGen.getProgram(api); + p.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); + auto program = api.createProgram(std::move(p)); + + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); - auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0); + auto defaultRenderTarget = api.createDefaultRenderTarget(0); // To test large buffers (which exercise a different code path) create an extra large // buffer. Only the first 3 vertices will be used. - TrianglePrimitive triangle(getDriverApi(), largeBuffers); + TrianglePrimitive triangle(api, largeBuffers); RenderPassParams params = {}; fullViewport(params); @@ -113,6 +125,7 @@ TEST_F(BackendTest, VertexBufferUpdate) { PipelineState state; state.program = program; + state.pipelineLayout.setLayout = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; @@ -121,11 +134,13 @@ TEST_F(BackendTest, VertexBufferUpdate) { // Create a uniform buffer. // We use STATIC here, even though the buffer is updated, to force the Metal backend to use a // GPU buffer, which is more interesting to test. - auto ubuffer = getDriverApi().createBufferObject(sizeof(MaterialParams) + 64, + auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64, BufferObjectBinding::UNIFORM, BufferUsage::STATIC); - getDriverApi().bindUniformBuffer(0, ubuffer); - getDriverApi().startCapture(0); + api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64); + api.bindDescriptorSet(descriptorSet, 0, {}); + + api.startCapture(0); // Upload uniforms. { @@ -139,11 +154,11 @@ TEST_F(BackendTest, VertexBufferUpdate) { delete sp; }; BufferDescriptor bd(tmp, sizeof(MaterialParams), cb); - getDriverApi().updateBufferObject(ubuffer, std::move(bd), 64); + api.updateBufferObject(ubuffer, std::move(bd), 64); } - getDriverApi().makeCurrent(swapChain, swapChain); - getDriverApi().beginFrame(0, 0, 0); + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); // Draw 10 triangles, updating the vertex buffer / index buffer each time. size_t triangleIndex = 0; @@ -171,22 +186,25 @@ TEST_F(BackendTest, VertexBufferUpdate) { params.flags.discardStart = TargetBufferFlags::NONE; } - getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); - getDriverApi().endRenderPass(); + api.beginRenderPass(defaultRenderTarget, params); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); + api.endRenderPass(); triangleIndex++; } - getDriverApi().flush(); - getDriverApi().commit(swapChain); - getDriverApi().endFrame(0); + api.flush(); + api.commit(swapChain); + api.endFrame(0); - getDriverApi().stopCapture(0); + api.stopCapture(0); - getDriverApi().destroyProgram(program); - getDriverApi().destroySwapChain(swapChain); - getDriverApi().destroyRenderTarget(defaultRenderTarget); + api.destroyProgram(program); + api.destroySwapChain(swapChain); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); + api.destroyBufferObject(ubuffer); + api.destroyRenderTarget(defaultRenderTarget); } executeCommands(); @@ -195,27 +213,41 @@ TEST_F(BackendTest, VertexBufferUpdate) { // This test renders two triangles in two separate draw calls. Between the draw calls, a uniform // buffer object is partially updated. TEST_F(BackendTest, BufferObjectUpdateWithOffset) { + auto& api = getDriverApi(); + // Create a platform-specific SwapChain and make it current. auto swapChain = createSwapChain(); - getDriverApi().makeCurrent(swapChain, swapChain); + api.makeCurrent(swapChain, swapChain); // Create a program. ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform); - Program p = shaderGen.getProgram(getDriverApi()); - p.uniformBlockBindings({{"params", 1}}); - auto program = getDriverApi().createProgram(std::move(p)); + Program p = shaderGen.getProgram(api); + p.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); + auto program = api.createProgram(std::move(p)); + + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + // Create a uniform buffer. // We use STATIC here, even though the buffer is updated, to force the Metal backend to use a // GPU buffer, which is more interesting to test. - auto ubuffer = getDriverApi().createBufferObject(sizeof(MaterialParams) + 64, + auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64, BufferObjectBinding::UNIFORM, BufferUsage::STATIC); - getDriverApi().bindUniformBuffer(0, ubuffer); + + api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64); + api.bindDescriptorSet(descriptorSet, 0, {}); // Create a render target. - auto colorTexture = getDriverApi().createTexture(SamplerType::SAMPLER_2D, 1, + auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT); - auto renderTarget = getDriverApi().createRenderTarget( + auto renderTarget = api.createRenderTarget( TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {}); // Upload uniforms for the first triangle. @@ -230,7 +262,7 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { delete sp; }; BufferDescriptor bd(tmp, sizeof(MaterialParams), cb); - getDriverApi().updateBufferObject(ubuffer, std::move(bd), 64); + api.updateBufferObject(ubuffer, std::move(bd), 64); } RenderPassParams params = {}; @@ -240,7 +272,8 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { params.flags.discardEnd = TargetBufferFlags::NONE; params.viewport.height = 512; params.viewport.width = 512; - renderTriangle(renderTarget, swapChain, program, params); + renderTriangle({ descriptorSetLayout }, + renderTarget, swapChain, program, params); // Upload uniforms for the second triangle. To test partial buffer updates, we'll only update // color.b, color.a, offset.x, and offset.y. @@ -255,29 +288,32 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { delete sp; }; BufferDescriptor bd((char*)tmp + offsetof(MaterialParams, color.b), sizeof(float) * 4, cb); - getDriverApi().updateBufferObject(ubuffer, std::move(bd), 64 + offsetof(MaterialParams, color.b)); + api.updateBufferObject(ubuffer, std::move(bd), 64 + offsetof(MaterialParams, color.b)); } params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; - renderTriangle(renderTarget, swapChain, program, params); + renderTriangle({ descriptorSetLayout }, + renderTarget, swapChain, program, params); static const uint32_t expectedHash = 91322442; readPixelsAndAssertHash( "BufferObjectUpdateWithOffset", 512, 512, renderTarget, expectedHash, true); - getDriverApi().flush(); - getDriverApi().commit(swapChain); - getDriverApi().endFrame(0); + api.flush(); + api.commit(swapChain); + api.endFrame(0); - getDriverApi().destroyProgram(program); - getDriverApi().destroySwapChain(swapChain); - getDriverApi().destroyBufferObject(ubuffer); - getDriverApi().destroyRenderTarget(renderTarget); - getDriverApi().destroyTexture(colorTexture); + api.destroyProgram(program); + api.destroySwapChain(swapChain); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); + api.destroyBufferObject(ubuffer); + api.destroyRenderTarget(renderTarget); + api.destroyTexture(colorTexture); // This ensures all driver commands have finished before exiting the test. - getDriverApi().finish(); + api.finish(); executeCommands(); diff --git a/filament/backend/test/test_ComputeBasic.cpp b/filament/backend/test/test_ComputeBasic.cpp index 70622595592..0e5febfa564 100644 --- a/filament/backend/test/test_ComputeBasic.cpp +++ b/filament/backend/test/test_ComputeBasic.cpp @@ -155,8 +155,9 @@ kernel void main0(device Output_data& output_data [[buffer(0)]], driver.dispatchCompute(ph, { groupCount, 1, 1 }); - driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 0); - driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 1); +// FIXME: we need a way to unbind the buffer in order to read from them +// driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 0); +// driver.unbindBuffer(BufferObjectBinding::SHADER_STORAGE, 1); float* const user = (float*)malloc(size); driver.readBufferSubData(output_data, 0, size, { user, size }); diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index 7babb407459..c30d6604545 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -19,12 +19,17 @@ #include "ShaderGenerator.h" #include "TrianglePrimitive.h" -#include "private/backend/SamplerGroup.h" +#include +#include #include #include #include +#include + +#include +#include #ifndef IOS #include @@ -37,14 +42,14 @@ using namespace image; // Shaders //////////////////////////////////////////////////////////////////////////////////////////////////// -static std::string fullscreenVs = R"(#version 450 core +static std::string const fullscreenVs = R"(#version 450 core layout(location = 0) in vec4 mesh_position; void main() { // Hack: move and scale triangle so that it covers entire viewport. gl_Position = vec4((mesh_position.xy + 0.5) * 5.0, 0.0, 1.0); })"; -static std::string fullscreenFs = R"(#version 450 core +static std::string const fullscreenFs = R"(#version 450 core precision mediump int; precision highp float; layout(location = 0) out vec4 fragColor; @@ -106,12 +111,12 @@ static void dumpScreenshot(DriverApi& dapi, Handle rt) { int w = kTexWidth, h = kTexHeight; const uint32_t* texels = (uint32_t*) buffer; sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0); - #ifndef IOS +#ifndef IOS LinearImage image(w, h, 4); image = toLinearWithAlpha(w, h, w * 4, (uint8_t*) buffer); std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc); ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", "feedback.png"); - #endif +#endif free(buffer); }; PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb); @@ -137,22 +142,48 @@ TEST_F(BackendTest, FeedbackLoops) { SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); - prog.uniformBlockBindings({{"params", 1}}); + prog.descriptorBindings(0, { + { "test_tex", DescriptorType::SAMPLER, 0 }, + { "Params", DescriptorType::UNIFORM_BUFFER, 1 } + }); program = api.createProgram(std::move(prog)); } + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }, + { + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 1, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + TrianglePrimitive const triangle(getDriverApi()); // Create a texture. auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; Handle const texture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage); + api.updateDescriptorSetTexture(descriptorSet, 0, texture, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST + }); + + // Create ubo + auto ubuffer = api.createBufferObject(sizeof(MaterialParams), + BufferObjectBinding::UNIFORM, BufferUsage::STATIC); + api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams)); + + api.bindDescriptorSet(descriptorSet, 0, {}); // Create a RenderTarget for each miplevel. Handle renderTargets[kNumLevels]; @@ -189,20 +220,10 @@ TEST_F(BackendTest, FeedbackLoops) { state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; state.program = program; - SamplerGroup samplers(1); - SamplerParams sparams = {}; - sparams.filterMag = SamplerMagFilter::LINEAR; - sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST; - samplers.setSampler(0, { texture, sparams }); - auto sgroup = - api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); - auto ubuffer = api.createBufferObject(sizeof(MaterialParams), - BufferObjectBinding::UNIFORM, BufferUsage::STATIC); + state.pipelineLayout.setLayout = { descriptorSetLayout }; + api.makeCurrent(swapChain, swapChain); api.beginFrame(0, 0, 0); - api.bindSamplers(0, sgroup); - api.bindUniformBuffer(0, ubuffer); // Downsample passes params.flags.discardStart = TargetBufferFlags::ALL; @@ -259,10 +280,15 @@ TEST_F(BackendTest, FeedbackLoops) { getDriver().purge(); } + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyProgram(program); api.destroySwapChain(swapChain); api.destroyTexture(texture); - for (auto rt : renderTargets) api.destroyRenderTarget(rt); + api.destroyBufferObject(ubuffer); + for (auto rt : renderTargets) { + api.destroyRenderTarget(rt); + } } const uint32_t expected = 0x70695aa1; diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index 2af57eb7196..8e7e3df7f3a 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -20,14 +20,18 @@ #include "TrianglePrimitive.h" #include "BackendTestUtils.h" +#include +#include + #include "private/filament/SamplerInterfaceBlock.h" -#include "private/backend/SamplerGroup.h" #include -#include #include +#include +#include + using namespace filament; using namespace filament::backend; @@ -300,17 +304,27 @@ TEST_F(BackendTest, UpdateImage2D) { SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH }} ) .build(); std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(t.textureFormat), fragmentTemplate); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); + prog.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + ProgramHandle const program = api.createProgram(std::move(prog)); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + // Create a Texture. auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE; Handle const texture = api.createTexture(SamplerType::SAMPLER_2D, 1, @@ -331,23 +345,21 @@ TEST_F(BackendTest, UpdateImage2D) { checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding)); } - SamplerGroup samplers(1); - samplers.setSampler(0, { texture, { + api.updateDescriptorSetTexture(descriptorSet, 0, texture, { .filterMag = SamplerMagFilter::NEAREST, - .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST } }); - - auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); + .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - api.bindSamplers(0, sgroup); + api.bindDescriptorSet(descriptorSet, 0, {}); - renderTriangle(defaultRenderTarget, swapChain, program); + renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash); api.commit(swapChain); api.endFrame(0); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyProgram(program); api.destroySwapChain(swapChain); api.destroyRenderTarget(defaultRenderTarget); @@ -377,15 +389,22 @@ TEST_F(BackendTest, UpdateImageSRGB) { SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentTemplate); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); + prog.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); ProgramHandle const program = api.createProgram(std::move(prog)); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); // Create a texture. Handle const texture = api.createTexture(SamplerType::SAMPLER_2D, 1, @@ -417,17 +436,14 @@ TEST_F(BackendTest, UpdateImageSRGB) { api.beginFrame(0, 0, 0); // Update samplers. - SamplerGroup samplers(1); - SamplerParams sparams = {}; - sparams.filterMag = SamplerMagFilter::LINEAR; - sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST; - samplers.setSampler(0, { texture, sparams }); - auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); + api.updateDescriptorSetTexture(descriptorSet, 0, texture, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST + }); - api.bindSamplers(0, sgroup); + api.bindDescriptorSet(descriptorSet, 0, {}); - renderTriangle(defaultRenderTarget, swapChain, program); + renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); static const uint32_t expectedHash = 359858623; readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash); @@ -436,7 +452,8 @@ TEST_F(BackendTest, UpdateImageSRGB) { api.commit(swapChain); api.endFrame(0); - api.destroySamplerGroup(sgroup); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyProgram(program); api.destroySwapChain(swapChain); api.destroyRenderTarget(defaultRenderTarget); @@ -465,15 +482,22 @@ TEST_F(BackendTest, UpdateImageMipLevel) { SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentUpdateImageMip); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); + prog.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); ProgramHandle const program = api.createProgram(std::move(prog)); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); // Create a texture with 3 mip levels. // Base level: 1024 @@ -489,17 +513,14 @@ TEST_F(BackendTest, UpdateImageMipLevel) { api.beginFrame(0, 0, 0); // Update samplers. - SamplerGroup samplers(1); - SamplerParams sparams = {}; - sparams.filterMag = SamplerMagFilter::LINEAR; - sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST; - samplers.setSampler(0, { texture, sparams }); - auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); + api.updateDescriptorSetTexture(descriptorSet, 0, texture, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST + }); - api.bindSamplers(0, sgroup); + api.bindDescriptorSet(descriptorSet, 0, {}); - renderTriangle(defaultRenderTarget, swapChain, program); + renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); static const uint32_t expectedHash = 3644679986; readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash); @@ -508,7 +529,8 @@ TEST_F(BackendTest, UpdateImageMipLevel) { api.commit(swapChain); api.endFrame(0); - api.destroySamplerGroup(sgroup); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyProgram(program); api.destroySwapChain(swapChain); api.destroyRenderTarget(defaultRenderTarget); @@ -539,15 +561,22 @@ TEST_F(BackendTest, UpdateImage3D) { SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); std::string fragment = stringReplace("{samplerType}", getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program prog = shaderGen.getProgram(api); - Program::Sampler psamplers[] = { utils::CString("test_tex"), 0 }; - prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0])); + prog.descriptorBindings(0, {{ "test_tex", DescriptorType::SAMPLER, 0 }}); ProgramHandle const program = api.createProgram(std::move(prog)); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); // Create a texture. Handle texture = api.createTexture(samplerType, 1, @@ -573,17 +602,14 @@ TEST_F(BackendTest, UpdateImage3D) { api.beginFrame(0, 0, 0); // Update samplers. - SamplerGroup samplers(1); - SamplerParams sparams = {}; - sparams.filterMag = SamplerMagFilter::LINEAR; - sparams.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST; - samplers.setSampler(0, { texture, sparams}); - auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); + api.updateDescriptorSetTexture(descriptorSet, 0, texture, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST + }); - api.bindSamplers(0, sgroup); + api.bindDescriptorSet(descriptorSet, 0, {}); - renderTriangle(defaultRenderTarget, swapChain, program); + renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); static const uint32_t expectedHash = 3644679986; readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash); @@ -592,7 +618,8 @@ TEST_F(BackendTest, UpdateImage3D) { api.commit(swapChain); api.endFrame(0); - api.destroySamplerGroup(sgroup); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyProgram(program); api.destroySwapChain(swapChain); api.destroyRenderTarget(defaultRenderTarget); diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp index 3c54839dad1..4ced788c0df 100644 --- a/filament/backend/test/test_MipLevels.cpp +++ b/filament/backend/test/test_MipLevels.cpp @@ -21,7 +21,11 @@ #include "TrianglePrimitive.h" #include "BackendTestUtils.h" -#include "private/backend/SamplerGroup.h" +#include +#include + +#include +#include namespace { @@ -87,8 +91,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { { ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform); Program p = shaderGen.getProgram(api); - Program::Sampler sampler{utils::CString("backend_test_sib_tex"), 0}; - p.setSamplerGroup(0, ShaderStageFlags::FRAGMENT, &sampler, 1); + p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}}); whiteProgram = api.createProgram(std::move(p)); } @@ -98,15 +101,23 @@ TEST_F(BackendTest, SetMinMaxLevel) { SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("backend_test_sib") .stageFlags(backend::ShaderStageFlags::FRAGMENT) - .add( {{"tex", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); Program p = shaderGen.getProgram(api); - Program::Sampler sampler{utils::CString("backend_test_sib_tex"), 0}; - p.setSamplerGroup(0, ShaderStageFlags::FRAGMENT, &sampler, 1); + p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}}); textureProgram = api.createProgram(std::move(p)); } + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + // Create a texture that has 4 mip levels. Each level is a different color. // Level 0: 128x128 (red) // Level 1: 64x64 (green) @@ -183,20 +194,17 @@ TEST_F(BackendTest, SetMinMaxLevel) { PipelineState state; state.program = textureProgram; + state.pipelineLayout.setLayout = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = SamplerCompareFunc::A; state.rasterState.culling = CullingMode::NONE; - SamplerGroup samplers(1); - SamplerParams samplerParams {}; - samplerParams.filterMag = SamplerMagFilter::NEAREST; - samplerParams.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST; - samplers.setSampler(0, { texture, samplerParams }); - backend::Handle samplerGroup = - api.createSamplerGroup(1, utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(samplerGroup, samplers.toBufferDescriptor(api)); - api.bindSamplers(0, samplerGroup); + api.updateDescriptorSetTexture(descriptorSet, 0, texture, { + .filterMag = SamplerMagFilter::NEAREST, + .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + + api.bindDescriptorSet(descriptorSet, 0, {}); // Render a triangle to the screen, sampling from mip level 1. // Because the min level is 1, the result color should be the white triangle drawn in the @@ -233,6 +241,8 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Cleanup. api.destroySwapChain(swapChain); api.destroyRenderTarget(renderTarget); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyTexture(texture); api.destroyProgram(whiteProgram); api.destroyProgram(textureProgram); diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index b8261434b23..3c8a7dfee8e 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -19,10 +19,14 @@ #include "ShaderGenerator.h" #include "TrianglePrimitive.h" -#include "private/backend/SamplerGroup.h" +#include +#include #include +#include +#include + namespace { //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -61,29 +65,38 @@ using namespace filament::backend; // Rendering an external image without setting any data should not crash. TEST_F(BackendTest, RenderExternalImageWithoutSet) { - TrianglePrimitive triangle(getDriverApi()); + auto& api = getDriverApi(); + + TrianglePrimitive triangle(api); auto swapChain = createSwapChain(); SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); // Create a program that samples a texture. - Program p = shaderGen.getProgram(getDriverApi()); - Program::Sampler sampler { utils::CString("test_tex"), 0 }; - p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1); - backend::Handle program = getDriverApi().createProgram(std::move(p)); + Program p = shaderGen.getProgram(api); + p.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + backend::Handle program = api.createProgram(std::move(p)); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); - backend::Handle defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0); + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + + backend::Handle defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a texture that will be backed by an external image. auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; const NativeView& view = getNativeView(); - backend::Handle texture = getDriverApi().createTexture( + backend::Handle texture = api.createTexture( SamplerType::SAMPLER_EXTERNAL, // target 1, // levels TextureFormat::RGBA8, // format @@ -102,63 +115,71 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { PipelineState state; state.program = program; + state.pipelineLayout.setLayout = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; state.rasterState.culling = CullingMode::NONE; - getDriverApi().startCapture(0); - getDriverApi().makeCurrent(swapChain, swapChain); - getDriverApi().beginFrame(0, 0, 0); + api.startCapture(0); + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); - SamplerGroup samplers(1); - samplers.setSampler(0, { texture, {} }); - backend::Handle samplerGroup = - getDriverApi().createSamplerGroup(1, utils::FixedSizeString<32>("Test")); - getDriverApi().updateSamplerGroup(samplerGroup, samplers.toBufferDescriptor(getDriverApi())); - getDriverApi().bindSamplers(0, samplerGroup); + api.updateDescriptorSetTexture(descriptorSet, 0, texture, {}); + api.bindDescriptorSet(descriptorSet, 0, {}); // Render a triangle. - getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); - getDriverApi().endRenderPass(); + api.beginRenderPass(defaultRenderTarget, params); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); + api.endRenderPass(); - getDriverApi().flush(); - getDriverApi().commit(swapChain); - getDriverApi().endFrame(0); + api.flush(); + api.commit(swapChain); + api.endFrame(0); - getDriverApi().stopCapture(0); + api.stopCapture(0); // Delete our resources. - getDriverApi().destroyTexture(texture); - getDriverApi().destroySamplerGroup(samplerGroup); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); + api.destroyTexture(texture); // Destroy frame resources. - getDriverApi().destroyProgram(program); - getDriverApi().destroyRenderTarget(defaultRenderTarget); + api.destroyProgram(program); + api.destroyRenderTarget(defaultRenderTarget); executeCommands(); } TEST_F(BackendTest, RenderExternalImage) { - TrianglePrimitive triangle(getDriverApi()); + auto& api = getDriverApi(); + + TrianglePrimitive triangle(api); auto swapChain = createSwapChain(); SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() .name("Test") .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) + .add( {{"tex", 0, SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) .build(); ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); // Create a program that samples a texture. - Program p = shaderGen.getProgram(getDriverApi()); - Program::Sampler sampler { utils::CString("test_tex"), 0 }; - p.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, &sampler, 1); - auto program = getDriverApi().createProgram(std::move(p)); + Program p = shaderGen.getProgram(api); + p.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + auto program = api.createProgram(std::move(p)); - backend::Handle defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::SAMPLER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + + backend::Handle defaultRenderTarget = api.createDefaultRenderTarget(0); // require users to create two Filament textures and have two material parameters // add a "plane" parameter to setExternalImage @@ -166,7 +187,7 @@ TEST_F(BackendTest, RenderExternalImage) { // Create a texture that will be backed by an external image. auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; const NativeView& view = getNativeView(); - backend::Handle texture = getDriverApi().createTexture( + backend::Handle texture = api.createTexture( SamplerType::SAMPLER_EXTERNAL, // target 1, // levels TextureFormat::RGBA8, // format @@ -209,8 +230,8 @@ TEST_F(BackendTest, RenderExternalImage) { } } - getDriverApi().setupExternalImage(pixBuffer); - getDriverApi().setExternalImage(texture, pixBuffer); + api.setupExternalImage(pixBuffer); + api.setExternalImage(texture, pixBuffer); // We're now free to release the buffer. CVBufferRelease(pixBuffer); @@ -224,40 +245,39 @@ TEST_F(BackendTest, RenderExternalImage) { PipelineState state; state.program = program; + state.pipelineLayout.setLayout = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; state.rasterState.culling = CullingMode::NONE; - getDriverApi().startCapture(0); - getDriverApi().makeCurrent(swapChain, swapChain); - getDriverApi().beginFrame(0, 0, 0); + api.startCapture(0); + api.makeCurrent(swapChain, swapChain); + api.beginFrame(0, 0, 0); + + api.updateDescriptorSetTexture(descriptorSet, 0, texture, {}); - SamplerGroup samplers(1); - samplers.setSampler(0, { texture, {} }); - backend::Handle samplerGroup = - getDriverApi().createSamplerGroup(1, utils::FixedSizeString<32>("Test")); - getDriverApi().updateSamplerGroup(samplerGroup, samplers.toBufferDescriptor(getDriverApi())); - getDriverApi().bindSamplers(0, samplerGroup); + api.bindDescriptorSet(descriptorSet, 0, {}); // Render a triangle. - getDriverApi().beginRenderPass(defaultRenderTarget, params); - getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1); - getDriverApi().endRenderPass(); + api.beginRenderPass(defaultRenderTarget, params); + api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); + api.endRenderPass(); - getDriverApi().flush(); - getDriverApi().commit(swapChain); - getDriverApi().endFrame(0); + api.flush(); + api.commit(swapChain); + api.endFrame(0); - getDriverApi().stopCapture(0); + api.stopCapture(0); // Delete our resources. - getDriverApi().destroyTexture(texture); - getDriverApi().destroySamplerGroup(samplerGroup); + api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSetLayout(descriptorSetLayout); + api.destroyTexture(texture); // Destroy frame resources. - getDriverApi().destroyProgram(program); - getDriverApi().destroyRenderTarget(defaultRenderTarget); + api.destroyProgram(program); + api.destroyRenderTarget(defaultRenderTarget); executeCommands(); } diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 363ef2d7c1f..9badecf682d 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -489,33 +489,11 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { * For standard morphing, A MorphTargetBuffer must be provided. * Standard morphing supports up to \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. * - * For legacy morphing, the attached VertexBuffer must provide data in the - * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only - * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must - * be enabled on the material definition: either via the legacyMorphing material attribute - * or by calling filamat::MaterialBuilder::useLegacyMorphing(). - * * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis * to advance the animation. */ Builder& morphing(MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept; - /** - * @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead - */ - Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, - size_t offset, size_t count) noexcept; - - /** - * @deprecated Use morphing(uint8_t level, size_t primitiveIndex, size_t offset, size_t count) instead - */ - inline Builder& morphing(uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) noexcept { - return morphing(level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); - } - /** * Specifies the the range of the MorphTargetBuffer to use with this primitive. * @@ -620,21 +598,6 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { friend class FEngine; friend class FRenderPrimitive; friend class FRenderableManager; - struct Entry { - VertexBuffer* UTILS_NULLABLE vertices = nullptr; - IndexBuffer* UTILS_NULLABLE indices = nullptr; - size_t offset = 0; - size_t count = 0; - MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr; - PrimitiveType type = PrimitiveType::TRIANGLES; - uint16_t blendOrder = 0; - bool globalBlendOrderEnabled = false; - struct { - MorphTargetBuffer* UTILS_NULLABLE buffer = nullptr; - size_t offset = 0; - size_t count = 0; - } morphing; - }; }; /** @@ -789,22 +752,10 @@ class UTILS_PUBLIC RenderableManager : public FilamentAPI { void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count); - /** @deprecated */ - void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer, size_t offset, size_t count); - - /** @deprecated */ - inline void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* UTILS_NONNULL morphTargetBuffer) { - setMorphTargetBufferAt(instance, level, primitiveIndex, morphTargetBuffer, 0, - morphTargetBuffer->getVertexCount()); - } - /** - * Get a MorphTargetBuffer to the given primitive or null if it doesn't exist. + * Get a MorphTargetBuffer to the given renderable or null if it doesn't exist. */ - MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBufferAt(Instance instance, - uint8_t level, size_t primitiveIndex) const noexcept; + MorphTargetBuffer* UTILS_NULLABLE getMorphTargetBuffer(Instance instance) const noexcept; /** * Gets the number of morphing in the given entity. diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index 0304253b905..8beb83c5e53 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -24,18 +24,28 @@ #include -#include #include #include #include #include #include #include +#include +#include +#include + +#include #include +#include -#include +#include #include +#include +#include + +#include +#include using namespace utils; using namespace filament::backend; @@ -62,12 +72,13 @@ constexpr std::pair shaderLanguageToTags(ShaderLanguage la // ------------------------------------------------------------------------------------------------ MaterialParser::MaterialParserDetails::MaterialParserDetails( - const utils::FixedCapacityVector& preferredLanguages, const void* data, + utils::FixedCapacityVector preferredLanguages, const void* data, size_t size) : mManagedBuffer(data, size), mChunkContainer(mManagedBuffer.data(), mManagedBuffer.size()), - mPreferredLanguages(preferredLanguages), - mMaterialChunk(mChunkContainer) {} + mPreferredLanguages(std::move(preferredLanguages)), + mMaterialChunk(mChunkContainer) { +} template UTILS_NOINLINE @@ -82,11 +93,29 @@ bool MaterialParser::MaterialParserDetails::getFromSimpleChunk( return false; } +MaterialParser::MaterialParserDetails::ManagedBuffer::ManagedBuffer(const void* start, size_t size) + : mStart(malloc(size)), mSize(size) { + memcpy(mStart, start, size); +} + +MaterialParser::MaterialParserDetails::ManagedBuffer::~ManagedBuffer() noexcept { + free(mStart); +} + // ------------------------------------------------------------------------------------------------ +template +bool MaterialParser::get(typename T::Container* container) const noexcept { + auto [start, end] = mImpl.mChunkContainer.getChunkRange(T::tag); + if (start == end) return false; + filaflat::Unflattener unflattener{ start, end }; + return T::unflatten(unflattener, container); +} + MaterialParser::MaterialParser(utils::FixedCapacityVector preferredLanguages, const void* data, size_t size) - : mImpl(preferredLanguages, data, size) {} + : mImpl(std::move(preferredLanguages), data, size) { +} ChunkContainer& MaterialParser::getChunkContainer() noexcept { return mImpl.mChunkContainer; @@ -158,25 +187,16 @@ bool MaterialParser::getCacheId(uint64_t* cacheId) const noexcept { return unflattener.read(cacheId); } -bool MaterialParser::getUIB(BufferInterfaceBlock* uib) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialUib); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkUniformInterfaceBlock::unflatten(unflattener, uib); +bool MaterialParser::getUIB(BufferInterfaceBlock* container) const noexcept { + return get(container); } -bool MaterialParser::getSIB(SamplerInterfaceBlock* sib) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSib); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkSamplerInterfaceBlock::unflatten(unflattener, sib); +bool MaterialParser::getSIB(SamplerInterfaceBlock* container) const noexcept { + return get(container); } -bool MaterialParser::getSubpasses(SubpassInfo* subpass) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSubpass); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkSubpassInterfaceBlock::unflatten(unflattener, subpass); +bool MaterialParser::getSubpasses(SubpassInfo* container) const noexcept { + return get(container); } bool MaterialParser::getShaderModels(uint32_t* value) const noexcept { @@ -187,43 +207,24 @@ bool MaterialParser::getMaterialProperties(uint64_t* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialProperties, value); } -bool MaterialParser::getUniformBlockBindings( - utils::FixedCapacityVector>* value) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialUniformBindings); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkUniformBlockBindings::unflatten(unflattener, value); -} - bool MaterialParser::getBindingUniformInfo(BindingUniformInfoContainer* container) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialBindingUniformInfo); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkBindingUniformInfo::unflatten(unflattener, container); + return get(container); } bool MaterialParser::getAttributeInfo(AttributeInfoContainer* container) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialAttributeInfo); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkAttributeInfo::unflatten(unflattener, container); + return get(container); } -bool MaterialParser::getSamplerBlockBindings( - SamplerGroupBindingInfoList* pSamplerGroupInfoList, - SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialSamplerBindings); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkSamplerBlockBindings::unflatten(unflattener, - pSamplerGroupInfoList, pSamplerBindingToNameMap); +bool MaterialParser::getDescriptorBindings(DescriptorBindingsContainer* container) const noexcept { + return get(container); } -bool MaterialParser::getConstants(utils::FixedCapacityVector* value) const noexcept { - auto [start, end] = mImpl.mChunkContainer.getChunkRange(filamat::MaterialConstants); - if (start == end) return false; - Unflattener unflattener(start, end); - return ChunkMaterialConstants::unflatten(unflattener, value); +bool MaterialParser::getDescriptorSetLayout(DescriptorSetLayoutContainer* container) const noexcept { + return get(container); +} + +bool MaterialParser::getConstants(utils::FixedCapacityVector* container) const noexcept { + return get(container); } bool MaterialParser::getPushConstants(utils::CString* structVarName, @@ -460,7 +461,9 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener, } for (uint64_t i = 0; i < numFields; i++) { + static_assert(sizeof(backend::descriptor_binding_t) == sizeof(uint8_t)); CString fieldName; + uint8_t fieldBinding = 0; uint8_t fieldType = 0; uint8_t fieldFormat = 0; uint8_t fieldPrecision = 0; @@ -470,6 +473,10 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener, return false; } + if (!unflattener.read(&fieldBinding)) { + return false; + } + if (!unflattener.read(&fieldType)) { return false; } @@ -486,7 +493,9 @@ bool ChunkSamplerInterfaceBlock::unflatten(Unflattener& unflattener, return false; } - builder.add({ fieldName.data(), fieldName.size() }, SamplerInterfaceBlock::Type(fieldType), + builder.add({ fieldName.data(), fieldName.size() }, + SamplerInterfaceBlock::Binding(fieldBinding), + SamplerInterfaceBlock::Type(fieldType), SamplerInterfaceBlock::Format(fieldFormat), SamplerInterfaceBlock::Precision(fieldPrecision), fieldMultisample); @@ -551,28 +560,6 @@ bool ChunkSubpassInterfaceBlock::unflatten(Unflattener& unflattener, return true; } -bool ChunkUniformBlockBindings::unflatten(filaflat::Unflattener& unflattener, - utils::FixedCapacityVector>* uniformBlockBindings) { - uint8_t count; - if (!unflattener.read(&count)) { - return false; - } - uniformBlockBindings->reserve(count); - - for (uint8_t i = 0; i < count; i++) { - CString name; - uint8_t binding; - if (!unflattener.read(&name)) { - return false; - } - if (!unflattener.read(&binding)) { - return false; - } - uniformBlockBindings->emplace_back(std::move(name), binding); - } - return true; -} - bool ChunkBindingUniformInfo::unflatten(filaflat::Unflattener& unflattener, MaterialParser::BindingUniformInfoContainer* bindingUniformInfo) { uint8_t bindingPointCount; @@ -585,6 +572,10 @@ bool ChunkBindingUniformInfo::unflatten(filaflat::Unflattener& unflattener, if (!unflattener.read(&index)) { return false; } + utils::CString uboName; + if (!unflattener.read(&uboName)) { + return false; + } uint8_t uniformCount; if (!unflattener.read(&uniformCount)) { return false; @@ -610,7 +601,7 @@ bool ChunkBindingUniformInfo::unflatten(filaflat::Unflattener& unflattener, } uniforms.push_back({ name, offset, size, UniformType(type) }); } - bindingUniformInfo->emplace_back(UniformBindingPoints(index), std::move(uniforms)); + bindingUniformInfo->emplace_back(index, std::move(uboName), std::move(uniforms)); } return true; } @@ -640,49 +631,91 @@ bool ChunkAttributeInfo::unflatten(filaflat::Unflattener& unflattener, return true; } -bool ChunkSamplerBlockBindings::unflatten(Unflattener& unflattener, - SamplerGroupBindingInfoList* pSamplerGroupBindingInfoList, - SamplerBindingToNameMap* pSamplerBindingToNameMap) { - assert_invariant(pSamplerGroupBindingInfoList && pSamplerBindingToNameMap); - SamplerGroupBindingInfoList& samplerGroupBindingInfoList = *pSamplerGroupBindingInfoList; - SamplerBindingToNameMap& samplerBindingToNameMap = *pSamplerBindingToNameMap; +bool ChunkDescriptorBindingsInfo::unflatten(filaflat::Unflattener& unflattener, + MaterialParser::DescriptorBindingsContainer* container) { - uint8_t count; - if (!unflattener.read(&count)) { + uint8_t setCount; + if (!unflattener.read(&setCount)) { return false; } - assert_invariant(count == utils::Enum::count()); - UTILS_NOUNROLL - for (size_t i = 0; i < count; i++) { - if (!unflattener.read(&samplerGroupBindingInfoList[i].bindingOffset)) { + for (size_t j = 0; j < setCount; j++) { + static_assert(sizeof(DescriptorSetBindingPoints) == sizeof(uint8_t)); + + DescriptorSetBindingPoints set; + if (!unflattener.read(reinterpret_cast(&set))) { return false; } - if (!unflattener.read((uint8_t *)&samplerGroupBindingInfoList[i].shaderStageFlags)) { + + uint8_t descriptorCount; + if (!unflattener.read(&descriptorCount)) { return false; } - if (!unflattener.read(&samplerGroupBindingInfoList[i].count)) { - return false; + + auto& descriptors = (*container)[+set]; + descriptors.reserve(descriptorCount); + for (size_t i = 0; i < descriptorCount; i++) { + utils::CString name; + if (!unflattener.read(&name)) { + return false; + } + uint8_t type; + if (!unflattener.read(&type)) { + return false; + } + uint8_t binding; + if (!unflattener.read(&binding)) { + return false; + } + descriptors.push_back({ + std::move(name), + backend::DescriptorType(type), + backend::descriptor_binding_t(binding)}); } } - if (!unflattener.read(&count)) { - return false; - } + return true; +} - samplerBindingToNameMap.reserve(count); - samplerBindingToNameMap.resize(count); - for (size_t i = 0; i < count; i++) { - uint8_t binding; - if (!unflattener.read(&binding)) { +bool ChunkDescriptorSetLayoutInfo::unflatten(filaflat::Unflattener& unflattener, + MaterialParser::DescriptorSetLayoutContainer* container) { + for (size_t j = 0; j < 2; j++) { + uint8_t descriptorCount; + if (!unflattener.read(&descriptorCount)) { return false; } - assert_invariant(binding < backend::MAX_SAMPLER_COUNT); - if (!unflattener.read(&samplerBindingToNameMap[binding])) { - return false; + auto& descriptors = (*container)[j].bindings; + descriptors.reserve(descriptorCount); + for (size_t i = 0; i < descriptorCount; i++) { + uint8_t type; + if (!unflattener.read(&type)) { + return false; + } + uint8_t stageFlags; + if (!unflattener.read(&stageFlags)) { + return false; + } + uint8_t binding; + if (!unflattener.read(&binding)) { + return false; + } + uint8_t flags; + if (!unflattener.read(&flags)) { + return false; + } + uint16_t count; + if (!unflattener.read(&count)) { + return false; + } + descriptors.push_back({ + backend::DescriptorType(type), + backend::ShaderStageFlags(stageFlags), + backend::descriptor_binding_t(binding), + backend::DescriptorFlags(flags), + count, + }); } } - return true; } diff --git a/filament/src/MaterialParser.h b/filament/src/MaterialParser.h index b10a549d8b3..544fdcf8107 100644 --- a/filament/src/MaterialParser.h +++ b/filament/src/MaterialParser.h @@ -23,7 +23,6 @@ #include #include -#include "../../libs/filamat/src/SamplerBindingMap.h" #include #include @@ -31,11 +30,14 @@ #include #include -#include -#include +#include +#include #include +#include +#include + namespace filaflat { class ChunkContainer; class Unflattener; @@ -76,23 +78,24 @@ class MaterialParser { bool getSubpasses(SubpassInfo* subpass) const noexcept; bool getShaderModels(uint32_t* value) const noexcept; bool getMaterialProperties(uint64_t* value) const noexcept; - bool getUniformBlockBindings(utils::FixedCapacityVector>* value) const noexcept; - bool getSamplerBlockBindings(SamplerGroupBindingInfoList* pSamplerGroupInfoList, - SamplerBindingToNameMap* pSamplerBindingToNameMap) const noexcept; bool getConstants(utils::FixedCapacityVector* value) const noexcept; bool getPushConstants(utils::CString* structVarName, utils::FixedCapacityVector* value) const noexcept; - using BindingUniformInfoContainer = utils::FixedCapacityVector< - std::pair>; - + using BindingUniformInfoContainer = utils::FixedCapacityVector>; bool getBindingUniformInfo(BindingUniformInfoContainer* container) const noexcept; using AttributeInfoContainer = utils::FixedCapacityVector< std::pair>; - bool getAttributeInfo(AttributeInfoContainer* container) const noexcept; + using DescriptorBindingsContainer = backend::Program::DescriptorSetInfo; + bool getDescriptorBindings(DescriptorBindingsContainer* container) const noexcept; + + using DescriptorSetLayoutContainer = std::array; + bool getDescriptorSetLayout(DescriptorSetLayoutContainer* container) const noexcept; + bool getDepthWriteSet(bool* value) const noexcept; bool getDepthWrite(bool* value) const noexcept; bool getDoubleSidedSet(bool* value) const noexcept; @@ -136,9 +139,13 @@ class MaterialParser { } private: + + template + bool get(typename T::Container* container) const noexcept; + struct MaterialParserDetails { MaterialParserDetails( - const utils::FixedCapacityVector& preferredLanguages, + utils::FixedCapacityVector preferredLanguages, const void* data, size_t size); template @@ -151,11 +158,8 @@ class MaterialParser { void* mStart = nullptr; size_t mSize = 0; public: - explicit ManagedBuffer(const void* start, size_t size) - : mStart(malloc(size)), mSize(size) { - memcpy(mStart, start, size); - } - ~ManagedBuffer() noexcept { free(mStart); } + explicit ManagedBuffer(const void* start, size_t size); + ~ManagedBuffer() noexcept; ManagedBuffer(ManagedBuffer const& rhs) = delete; ManagedBuffer& operator=(ManagedBuffer const& rhs) = delete; void* data() const noexcept { return mStart; } @@ -181,40 +185,55 @@ class MaterialParser { struct ChunkUniformInterfaceBlock { static bool unflatten(filaflat::Unflattener& unflattener, BufferInterfaceBlock* uib); + using Container = BufferInterfaceBlock; + static filamat::ChunkType const tag = filamat::MaterialUib; }; struct ChunkSamplerInterfaceBlock { static bool unflatten(filaflat::Unflattener& unflattener, SamplerInterfaceBlock* sib); + using Container = SamplerInterfaceBlock; + static filamat::ChunkType const tag = filamat::MaterialSib; }; struct ChunkSubpassInterfaceBlock { static bool unflatten(filaflat::Unflattener& unflattener, SubpassInfo* sib); -}; - -struct ChunkUniformBlockBindings { - static bool unflatten(filaflat::Unflattener& unflattener, - utils::FixedCapacityVector>* uniformBlockBindings); + using Container = SubpassInfo; + static filamat::ChunkType const tag = filamat::MaterialSubpass; }; struct ChunkBindingUniformInfo { static bool unflatten(filaflat::Unflattener& unflattener, MaterialParser::BindingUniformInfoContainer* bindingUniformInfo); + using Container = MaterialParser::BindingUniformInfoContainer; + static filamat::ChunkType const tag = filamat::MaterialBindingUniformInfo; }; struct ChunkAttributeInfo { static bool unflatten(filaflat::Unflattener& unflattener, MaterialParser::AttributeInfoContainer* attributeInfoContainer); + using Container = MaterialParser::AttributeInfoContainer; + static filamat::ChunkType const tag = filamat::MaterialAttributeInfo; +}; + +struct ChunkDescriptorBindingsInfo { + static bool unflatten(filaflat::Unflattener& unflattener, + MaterialParser::DescriptorBindingsContainer* container); + using Container = MaterialParser::DescriptorBindingsContainer; + static filamat::ChunkType const tag = filamat::MaterialDescriptorBindingsInfo; }; -struct ChunkSamplerBlockBindings { +struct ChunkDescriptorSetLayoutInfo { static bool unflatten(filaflat::Unflattener& unflattener, - SamplerGroupBindingInfoList* pSamplerGroupBindingInfoList, - SamplerBindingToNameMap* pSamplerBindingToNameMap); + MaterialParser::DescriptorSetLayoutContainer* container); + using Container = MaterialParser::DescriptorSetLayoutContainer; + static filamat::ChunkType const tag = filamat::MaterialDescriptorSetLayoutInfo; }; struct ChunkMaterialConstants { static bool unflatten(filaflat::Unflattener& unflattener, utils::FixedCapacityVector* materialConstants); + using Container = utils::FixedCapacityVector; + static filamat::ChunkType const tag = filamat::MaterialConstants; }; struct ChunkMaterialPushConstants { diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 85e21d6832e..44a4231604f 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -26,6 +26,8 @@ #include "details/Engine.h" +#include "ds/SsrPassDescriptorSet.h" + #include "fg/FrameGraph.h" #include "fg/FrameGraphId.h" #include "fg/FrameGraphResources.h" @@ -33,7 +35,6 @@ #include "fsr.h" #include "FrameHistory.h" -#include "PerViewUniforms.h" #include "RenderPass.h" #include "details/Camera.h" @@ -187,12 +188,18 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine) UTILS_NOINLINE std::pair PostProcessManager::PostProcessMaterial::getPipelineState( - FEngine& engine, Variant::type_t variantKey) const noexcept { + FEngine& engine, + Variant::type_t variantKey) const noexcept { FMaterial* const material = getMaterial(engine); material->prepareProgram(Variant{ variantKey }); return {{ .program = material->getProgram(Variant{ variantKey }), .vertexBufferInfo = engine.getFullScreenVertexBuffer()->getVertexBufferInfoHandle(), + .pipelineLayout = { .setLayout = { + material->getPerViewDescriptorSetLayout().getHandle(), + engine.getPerRenderableDescriptorSetLayout().getHandle(), + material->getDescriptorSetLayout().getHandle() + } }, .rasterState = material->getRasterState() }, material->getDefaultInstance()->getScissor() }; } @@ -238,6 +245,7 @@ PostProcessManager::PostProcessManager(FEngine& engine) noexcept : mEngine(engine), mWorkaroundSplitEasu(false), mWorkaroundAllowReadOnlyAncillaryFeedbackLoop(false) { + // don't use Engine here, it's not fully initialized yet } PostProcessManager::~PostProcessManager() noexcept = default; @@ -324,6 +332,9 @@ void PostProcessManager::init() noexcept { //debugRegistry.registerProperty("d.ssao.kernelSize", &engine.debug.ssao.kernelSize); //debugRegistry.registerProperty("d.ssao.stddev", &engine.debug.ssao.stddev); + mSsrPassDescriptorSet.init(engine); + mPostProcessDescriptorSet.init(engine); + mWorkaroundSplitEasu = driver.isWorkaroundNeeded(Workaround::SPLIT_EASU); mWorkaroundAllowReadOnlyAncillaryFeedbackLoop = @@ -362,12 +373,16 @@ void PostProcessManager::init() noexcept { void PostProcessManager::terminate(DriverApi& driver) noexcept { FEngine& engine = mEngine; driver.destroyTexture(mStarburstTexture); + auto first = mMaterialRegistry.begin(); auto last = mMaterialRegistry.end(); while (first != last) { first.value().terminate(engine); ++first; } + + mPostProcessDescriptorSet.terminate(driver); + mSsrPassDescriptorSet.terminate(driver); } backend::Handle PostProcessManager::getOneTexture() const { @@ -391,6 +406,8 @@ void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out, backend::PipelineState const& pipeline, backend::Viewport const& scissor, DriverApi& driver) const noexcept { + bindPostProcessDescriptorSet(driver); + assert_invariant( ((out.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) && !pipeline.rasterState.depthWrite) @@ -472,12 +489,14 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph }); }, [=, passBuilder = passBuilder](FrameGraphResources const& resources, - auto const&, DriverApi&) mutable { + auto const&, DriverApi& driver) mutable { Variant structureVariant(Variant::DEPTH_VARIANT); structureVariant.setPicking(config.picking); auto out = resources.getRenderPassInfo(); + bindPostProcessDescriptorSet(driver); + passBuilder.renderFlags(structureRenderFlags); passBuilder.variant(structureVariant); passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SSAO); @@ -533,7 +552,6 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, RenderPassBuilder const& passBuilder, FrameHistory const& frameHistory, CameraInfo const& cameraInfo, - PerViewUniforms& uniforms, FrameGraphId structure, ScreenSpaceReflectionsOptions const& options, FrameGraphTexture::Descriptor const& desc) noexcept { @@ -593,10 +611,14 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, }, [this, projection = cameraInfo.projection, userViewMatrix = cameraInfo.getUserViewMatrix(), uvFromClipMatrix, historyProjection, - options, &uniforms, passBuilder = passBuilder] + options, passBuilder = passBuilder] (FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable { + // set our frame uniforms in our descriptor-set + assert_invariant(mFrameUniforms); + mSsrPassDescriptorSet.setFrameUniforms(*mFrameUniforms); + // set structure sampler - uniforms.prepareStructure(data.structure ? + mSsrPassDescriptorSet.prepareStructure(data.structure ? resources.getTexture(data.structure) : getOneTexture()); // set screen-space reflections and screen-space refractions @@ -607,9 +629,11 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, // the history sampler is a regular texture2D TextureHandle const history = data.history ? resources.getTexture(data.history) : getZeroTexture(); - uniforms.prepareHistorySSR(history, reprojection, uvFromViewMatrix, options); + mSsrPassDescriptorSet.prepareHistorySSR(history, reprojection, uvFromViewMatrix, options); + + mSsrPassDescriptorSet.commit(mEngine); - uniforms.commit(driver); + mSsrPassDescriptorSet.bind(driver); auto out = resources.getRenderPassInfo(); @@ -1171,7 +1195,7 @@ FrameGraphId PostProcessManager::gaussianBlurPass(FrameGraph& mi->setParameter("layer", 0.0f); mi->setParameter("axis", float2{ 0, 1.0f / tempDesc.height }); mi->commit(driver); - // we don't need to call use() here, since it's the same material + mi->use(driver); render(hwOutRT, separableGaussianBlur.getPipelineState(mEngine), driver); }); @@ -1622,6 +1646,7 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, FMaterialInstance* const mi = material.getMaterialInstance(mEngine); mi->setParameter("color", inOutColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->commit(driver); mi->use(driver); auto const pipeline = material.getPipelineState(mEngine, variant); @@ -2316,6 +2341,7 @@ FrameGraphId PostProcessManager::customResolveUncompressPass( customResolvePrepareSubpass(driver, CustomResolveOp::UNCOMPRESS); auto out = resources.getRenderPassInfo(); out.params.subpassMask = 1; + bindPostProcessDescriptorSet(driver); driver.beginRenderPass(out.target, out.params); customResolveSubpass(driver); driver.endRenderPass(); @@ -2524,13 +2550,12 @@ FrameGraphId PostProcessManager::fxaa(FrameGraph& fg, return ppFXAA->output; } -void PostProcessManager::prepareTaa(FrameGraph& fg, +void PostProcessManager::TaaJitterCamera( filament::Viewport const& svp, TemporalAntiAliasingOptions const& taaOptions, FrameHistory& frameHistory, FrameHistoryEntry::TemporalAA FrameHistoryEntry::*pTaa, - CameraInfo* inoutCameraInfo, - PerViewUniforms& uniforms) const noexcept { + CameraInfo* inoutCameraInfo) const noexcept { auto const& previous = frameHistory.getPrevious().*pTaa; auto& current = frameHistory.getCurrent().*pTaa; @@ -2575,12 +2600,6 @@ void PostProcessManager::prepareTaa(FrameGraph& fg, // VERTEX_DOMAIN_DEVICE doesn't apply the projection, but it still needs this // clip transform, so we apply it separately (see main.vs) inoutCameraInfo->clipTransform.zw -= jitterInClipSpace; - - fg.addTrivialSideEffectPass("Jitter Camera", - [=, &uniforms] (DriverApi& driver) { - uniforms.prepareCamera(mEngine, *inoutCameraInfo); - uniforms.commit(driver); - }); } void PostProcessManager::configureTemporalAntiAliasingMaterial( @@ -2740,6 +2759,7 @@ FrameGraphId PostProcessManager::taa(FrameGraph& fg, out.params.subpassMask = 1; } auto const pipeline = material.getPipelineState(mEngine, variant); + bindPostProcessDescriptorSet(driver); driver.beginRenderPass(out.target, out.params); driver.scissor(pipeline.second); driver.draw(pipeline.first, mEngine.getFullScreenRenderPrimitive(), 0, 3, 1); @@ -2969,6 +2989,7 @@ FrameGraphId PostProcessManager::upscale(FrameGraph& fg, bool enableTranslucentBlending(pipeline0.first); enableTranslucentBlending(pipeline1.first); } + bindPostProcessDescriptorSet(driver); driver.beginRenderPass(out.target, out.params); driver.scissor(pipeline0.second); driver.draw(pipeline0.first, fullScreenRenderPrimitive, 0, 3, 1); diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 76f5bcd325f..7bed969cc99 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -21,26 +21,42 @@ #include "FrameHistory.h" +#include "ds/DescriptorSetLayout.h" +#include "ds/PostProcessDescriptorSet.h" +#include "ds/SsrPassDescriptorSet.h" +#include "ds/TypedUniformBuffer.h" + #include #include +#include #include +#include #include +#include #include #include -#include +#include +#include + +#include #include #include #include +#include #include #include +#include #include +#include +#include + namespace filament { class FColorGrading; @@ -48,7 +64,6 @@ class FEngine; class FMaterial; class FMaterialInstance; class FrameGraph; -class PerViewUniforms; class RenderPass; class RenderPassBuilder; struct CameraInfo; @@ -88,6 +103,13 @@ class PostProcessManager { void init() noexcept; void terminate(backend::DriverApi& driver) noexcept; + void bindPostProcessDescriptorSet(backend::DriverApi& driver) const noexcept { + assert_invariant(mFrameUniforms); + if (mFrameUniforms) { + mPostProcessDescriptorSet.setFrameUniforms(driver, *mFrameUniforms); + mPostProcessDescriptorSet.bind(driver); + } + } void configureTemporalAntiAliasingMaterial( TemporalAntiAliasingOptions const& taaOptions) noexcept; @@ -108,7 +130,6 @@ class PostProcessManager { RenderPassBuilder const& passBuilder, FrameHistory const& frameHistory, CameraInfo const& cameraInfo, - PerViewUniforms& uniforms, FrameGraphId structure, ScreenSpaceReflectionsOptions const& options, FrameGraphTexture::Descriptor const& desc) noexcept; @@ -215,13 +236,12 @@ class PostProcessManager { backend::TextureFormat outFormat, bool translucent) noexcept; // Temporal Anti-aliasing - void prepareTaa(FrameGraph& fg, + void TaaJitterCamera( filament::Viewport const& svp, TemporalAntiAliasingOptions const& taaOptions, FrameHistory& frameHistory, FrameHistoryEntry::TemporalAA FrameHistoryEntry::*pTaa, - CameraInfo* inoutCameraInfo, - PerViewUniforms& uniforms) const noexcept; + CameraInfo* inoutCameraInfo) const noexcept; FrameGraphId taa(FrameGraph& fg, FrameGraphId input, @@ -361,9 +381,17 @@ class PostProcessManager { render(out, combo.first, combo.second, driver); } + void setFrameUniforms(TypedUniformBuffer& uniforms) noexcept { + mFrameUniforms = std::addressof(uniforms); + } + private: FEngine& mEngine; + mutable SsrPassDescriptorSet mSsrPassDescriptorSet; + mutable PostProcessDescriptorSet mPostProcessDescriptorSet; + TypedUniformBuffer* mFrameUniforms = nullptr; + struct BilateralPassConfig { uint8_t kernelSize = 11; bool bentNormals = false; diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index ee5092370d0..ba463fc662c 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -26,6 +26,8 @@ #include "components/RenderableManager.h" +#include "ds/DescriptorSet.h" + #include #include #include @@ -80,8 +82,7 @@ RenderPassBuilder& RenderPassBuilder::customCommand( } RenderPass RenderPassBuilder::build(FEngine& engine) { - FILAMENT_CHECK_POSTCONDITION(mRenderableSoa) - << "RenderPassBuilder::geometry() hasn't been called"; + assert_invariant(mRenderableSoa); assert_invariant(mScissorViewport.width <= std::numeric_limits::max()); assert_invariant(mScissorViewport.height <= std::numeric_limits::max()); return RenderPass{ engine, *this }; @@ -91,15 +92,23 @@ RenderPass RenderPassBuilder::build(FEngine& engine) { void RenderPass::BufferObjectHandleDeleter::operator()( backend::BufferObjectHandle handle) noexcept { - if (handle) { + if (handle) { // this is common case driver.get().destroyBufferObject(handle); } } +void RenderPass::DescriptorSetHandleDeleter::operator()( + backend::DescriptorSetHandle handle) noexcept { + if (handle) { // this is common case + driver.get().destroyDescriptorSet(handle); + } +} + // ------------------------------------------------------------------------------------------------ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept : mRenderableSoa(*builder.mRenderableSoa), + mColorPassDescriptorSet(builder.mColorPassDescriptorSet), mScissorViewport(builder.mScissorViewport), mCustomCommands(engine.getPerRenderPassArena()) { @@ -133,7 +142,6 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce mCommandEnd = curr + commandCount + customCommandCount; appendCommands(engine, { curr, commandCount }, - builder.mUboHandle, builder.mVisibleRenderables, builder.mCommandTypeFlags, builder.mFlags, @@ -144,7 +152,7 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce if (builder.mCustomCommands.has_value()) { Command* p = curr + commandCount; - for (auto [channel, passId, command, order, fn]: builder.mCustomCommands.value()) { + for (auto const& [channel, passId, command, order, fn]: builder.mCustomCommands.value()) { appendCustomCommand(p++, channel, passId, command, order, fn); } } @@ -153,7 +161,7 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce sortCommands(builder.mArena); if (engine.isAutomaticInstancingEnabled()) { - uint32_t stereoscopicEyeCount = 1; + int32_t stereoscopicEyeCount = 1; if (builder.mFlags & IS_INSTANCED_STEREOSCOPIC) { stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount; } @@ -162,8 +170,7 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce } // this destructor is actually heavy because it inlines ~vector<> -RenderPass::~RenderPass() noexcept { -} +RenderPass::~RenderPass() noexcept = default; void RenderPass::resize(Arena& arena, size_t count) noexcept { if (mCommandBegin) { @@ -174,7 +181,6 @@ void RenderPass::resize(Arena& arena, size_t count) noexcept { void RenderPass::appendCommands(FEngine& engine, Slice commands, - backend::BufferObjectHandle const uboHandle, utils::Range const vr, CommandTypeFlags const commandTypeFlags, RenderFlags const renderFlags, @@ -207,12 +213,11 @@ void RenderPass::appendCommands(FEngine& engine, auto stereoscopicEyeCount = engine.getConfig().stereoscopicEyeCount; auto work = [commandTypeFlags, curr, &soa, - boh = uboHandle, variant, renderFlags, visibilityMask, cameraPosition, cameraForwardVector, stereoscopicEyeCount] (uint32_t startIndex, uint32_t indexCount) { RenderPass::generateCommands(commandTypeFlags, curr, - soa, { startIndex, startIndex + indexCount }, boh, + soa, { startIndex, startIndex + indexCount }, variant, renderFlags, visibilityMask, cameraPosition, cameraForwardVector, stereoscopicEyeCount); }; @@ -310,18 +315,22 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES; while (curr != last) { - - // Currently, if we have skinnning or morphing, we can't use auto instancing. This is - // because the morphing/skinning data for comparison is not easily accessible. - // Additionally, we can't have a different skinning/morphing per instance anyway. - // And thirdly, the info.index meaning changes with instancing, it is the index into - // the instancing buffer no longer the index into the soa. + // Currently, if we have skinning or morphing, we can't use auto instancing. This is + // because the morphing/skinning data for comparison is not easily accessible; and also + // because we're assuming that the per-renderable descriptor-set only has the + // OBJECT_UNIFORMS descriptor active (i.e. the skinning/morphing descriptors are unused). + // We also can't use auto-instancing if manual- or hybrid- instancing is used. + // TODO: support auto-instancing for skinning/morphing Command const* e = curr + 1; - if (UTILS_LIKELY(!curr->info.hasSkinning && !curr->info.hasMorphing)) { + if (UTILS_LIKELY( + !curr->info.hasSkinning && !curr->info.hasMorphing && + curr->info.instanceCount <= 1)) + { + assert_invariant(!curr->info.hasHybridInstancing); // we can't have nice things! No more than maxInstanceCount due to UBO size limits e = std::find_if_not(curr, std::min(last, curr + maxInstanceCount), [lhs = *curr](Command const& rhs) { - // primitives must be identical to be instanced. + // primitives must be identical to be instanced // Currently, instancing doesn't support skinning/morphing. return lhs.info.mi == rhs.info.mi && lhs.info.rph == rhs.info.rph && @@ -339,24 +348,45 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no if (UTILS_UNLIKELY(instanceCount > 1)) { drawCallsSavedCount += instanceCount - 1; + auto& driver = engine.getDriverApi(); + // allocate our staging buffer only if needed if (UTILS_UNLIKELY(!stagingBuffer)) { - - // create a temporary UBO for instancing - size_t const count = mCommandEnd - mCommandBegin; + // Create a temporary UBO for holding the per-renderable data of each primitive, + // The `curr->info.index` is updated so that this (now instanced) command can + // bind the UBO in the right place (where the per-instance data is). + // The lifetime of this object is the longest of this RenderPass and all its + // executors. + + // The maximum size we need for out buffer is to accommodate the remaining number + // of draw commands. + size_t const count = last - curr; mInstancedUboHandle = BufferObjectSharedHandle{ - engine.getDriverApi().createBufferObject( + driver.createBufferObject( count * sizeof(PerRenderableData) + sizeof(PerRenderableUib), - BufferObjectBinding::UNIFORM, BufferUsage::STATIC), - engine.getDriverApi() }; + BufferObjectBinding::UNIFORM, BufferUsage::STATIC), driver }; // TODO: use stream inline buffer for small sizes // TODO: use a pool for larger heap buffers // buffer large enough for all instances data - stagingBufferSize = sizeof(PerRenderableData) * (last - curr); + stagingBufferSize = count * sizeof(PerRenderableData); stagingBuffer = (PerRenderableData*)::malloc(stagingBufferSize); uboData = mRenderableSoa.data(); assert_invariant(uboData); + + // We also need a descriptor-set to hold the custom UBO. This works because + // we currently assume the descriptor-set only needs to hold this UBO in the + // instancing case (it wouldn't be true if we supported skinning/morphing, and + // in this case we would need to preserve the default descriptor-set content). + // This has the same lifetime as the UBO (see above). + mInstancedDescriptorSetHandle = DescriptorSetSharedHandle{ + driver.createDescriptorSet( + engine.getPerRenderableDescriptorSetLayout().getHandle()), + driver + }; + driver.updateDescriptorSetBuffer(mInstancedDescriptorSetHandle, + +PerRenderableBindingPoints::OBJECT_UNIFORMS, + mInstancedUboHandle, 0, sizeof(PerRenderableUib)); } // copy the ubo data to a staging buffer @@ -369,7 +399,7 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no // make the first command instanced curr[0].info.instanceCount = instanceCount * eyeCount; curr[0].info.index = instancedPrimitiveOffset; - curr[0].info.boh = mInstancedUboHandle; + curr[0].info.dsh = mInstancedDescriptorSetHandle; instancedPrimitiveOffset += instanceCount; @@ -384,8 +414,8 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena, int32_t eyeCount) no } if (UTILS_UNLIKELY(firstSentinel)) { - //slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of " - // << mCommandEnd - mCommandBegin << io::endl; +// slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of " +// << mCommandEnd - mCommandBegin << io::endl; // we have instanced primitives DriverApi& driver = engine.getDriverApi(); @@ -464,10 +494,10 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant, /* static */ UTILS_NOINLINE void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* const commands, - FScene::RenderableSoa const& soa, Range range, - backend::BufferObjectHandle renderablesUbo, - Variant variant, RenderFlags renderFlags, - FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward, + FScene::RenderableSoa const& soa, Range const range, + Variant const variant, RenderFlags const renderFlags, + FScene::VisibleMaskType const visibilityMask, + float3 const cameraPosition, float3 const cameraForward, uint8_t stereoEyeCount) noexcept { SYSTRACE_CALL(); @@ -499,13 +529,13 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co switch (commandTypeFlags & (CommandTypeFlags::COLOR | CommandTypeFlags::DEPTH)) { case CommandTypeFlags::COLOR: curr = generateCommandsImpl(commandTypeFlags, curr, - soa, range, renderablesUbo, + soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward, stereoEyeCount); break; case CommandTypeFlags::DEPTH: curr = generateCommandsImpl(commandTypeFlags, curr, - soa, range, renderablesUbo, + soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward, stereoEyeCount); break; @@ -529,7 +559,6 @@ UTILS_NOINLINE RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags, Command* UTILS_RESTRICT curr, FScene::RenderableSoa const& UTILS_RESTRICT soa, Range range, - backend::BufferObjectHandle renderablesUbo, Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward, uint8_t stereoEyeCount) noexcept { @@ -564,6 +593,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla auto const* const UTILS_RESTRICT soaMorphing = soa.data(); auto const* const UTILS_RESTRICT soaVisibilityMask = soa.data(); auto const* const UTILS_RESTRICT soaInstanceInfo = soa.data(); + auto const* const UTILS_RESTRICT soaDescriptorSet = soa.data(); Command cmd; @@ -646,6 +676,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmd.info.hasMorphing = (bool)morphing.handle; cmd.info.hasSkinning = (bool)skinning.handle; + assert_invariant(cmd.info.hasHybridInstancing || cmd.info.instanceCount <= 1); + // soaInstanceInfo[i].count is the number of instances the user has requested, either for // manual or hybrid instancing. Instanced stereo multiplies the number of instances by the // eye count. @@ -653,13 +685,12 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmd.info.instanceCount *= stereoEyeCount; } - if (cmd.info.hasHybridInstancing) { - // with hybrid instancing, we already know which UBO to use - cmd.info.boh = soaInstanceInfo[i].handle; - } else { - // with no- or user- instancing, we can only know after instanceify() - cmd.info.boh = renderablesUbo; - } + // soaDescriptorSet[i] is either populated with a common descriptor-set or truly with + // a per-renderable one, depending on for e.g. skinning/morphing/instancing. + cmd.info.dsh = soaDescriptorSet[i]; + + // always set the skinningOffset, even when skinning is off, this doesn't cost anything. + cmd.info.skinningOffset = soaSkinning[i].offset * sizeof(PerRenderableBoneUib::BoneData); const bool shadowCaster = soaVisibility[i].castShadows & hasShadowing; const bool writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster; @@ -669,9 +700,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla * This is our hot loop. It's written to avoid branches. * When modifying this code, always ensure it stays efficient. */ - for (size_t pi = 0, c = primitives.size(); pi < c; ++pi) { - auto const& primitive = primitives[pi]; - auto const& morphTargets = morphing.targets[pi]; + for (auto const& primitive: primitives) { FMaterialInstance const* const mi = primitive.getMaterialInstance(); FMaterial const* const ma = mi->getMaterial(); @@ -684,8 +713,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmd.info.indexOffset = primitive.getIndexOffset(); cmd.info.indexCount = primitive.getIndexCount(); cmd.info.type = primitive.getPrimitiveType(); - cmd.info.morphTargetBuffer = morphTargets.buffer->getHwHandle(); - cmd.info.morphingOffset = morphTargets.offset; + cmd.info.morphingOffset = primitive.getMorphingBufferOffset(); if constexpr (isColorPass) { RenderPass::setupColorCommand(cmd, renderableVariant, mi, inverseFrontFaces); @@ -857,6 +885,7 @@ void RenderPass::Executor::execute(FEngine& engine, SYSTRACE_CONTEXT(); DriverApi& driver = engine.getDriverApi(); + size_t const capacity = engine.getMinCommandBufferSize(); CircularBuffer const& circularBuffer = driver.getCircularBuffer(); @@ -865,7 +894,7 @@ void RenderPass::Executor::execute(FEngine& engine, bool const scissorOverride = mScissorOverride; if (UTILS_UNLIKELY(scissorOverride)) { - // initialize with scissor overide + // initialize with scissor override driver.scissor(mScissor); } @@ -875,9 +904,11 @@ void RenderPass::Executor::execute(FEngine& engine, .polygonOffset = mPolygonOffset, }; + pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_RENDERABLE] = + engine.getPerRenderableDescriptorSetLayout().getHandle(); + PipelineState currentPipeline{}; Handle currentPrimitiveHandle{}; - bool rebindPipeline = true; FMaterialInstance const* UTILS_RESTRICT mi = nullptr; FMaterial const* UTILS_RESTRICT ma = nullptr; @@ -942,93 +973,51 @@ void RenderPass::Executor::execute(FEngine& engine, ma = mi->getMaterial(); - if (UTILS_LIKELY(!scissorOverride)) { - backend::Viewport scissor = mi->getScissor(); - if (UTILS_UNLIKELY(mi->hasScissor())) { - scissor = applyScissorViewport(mScissorViewport, scissor); - } - driver.scissor(scissor); - } - - if (UTILS_LIKELY(!polygonOffsetOverride)) { - pipeline.polygonOffset = mi->getPolygonOffset(); - } + if (UTILS_LIKELY(!scissorOverride)) { + backend::Viewport scissor = mi->getScissor(); + if (UTILS_UNLIKELY(mi->hasScissor())) { + scissor = applyScissorViewport(mScissorViewport, scissor); + } + driver.scissor(scissor); + } + + if (UTILS_LIKELY(!polygonOffsetOverride)) { + pipeline.polygonOffset = mi->getPolygonOffset(); + } pipeline.stencilState = mi->getStencilState(); - mi->use(driver); - // FIXME: MaterialInstance changed (not necessarily the program though), - // however, texture bindings may have changed and currently we need to - // rebind the pipeline when that happens. - rebindPipeline = true; + // Each material has its own version of the per-view descriptor-set layout, + // because it depends on the material features (e.g. lit/unlit) + pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_VIEW] = + ma->getPerViewDescriptorSetLayout(info.materialVariant).getHandle(); + + // Each material has a per-material descriptor-set layout which encodes the + // material's parameters (ubo and samplers) + pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] = + ma->getDescriptorSetLayout().getHandle(); + + // If we have a ColorPassDescriptorSet we use it to bind the per-view + // descriptor-set (ideally only if it changed). If we don't, it means + // the descriptor-set is already bound and the layout we got from the + // material above should match. This is the case for situations where we + // have a known per-view descriptor-set layout, e.g.: shadow-maps, ssr and + // structure passes. + + if (mColorPassDescriptorSet) { + // We have a ColorPassDescriptorSet, we need to go through it for binding + // the per-view descriptor-set because its layout can change based on the + // material. + mColorPassDescriptorSet->bind(driver, ma->getPerViewLayoutIndex()); + } + + // Each MaterialInstance has its own descriptor set. This binds it. + mi->use(driver); } assert_invariant(ma); pipeline.program = ma->getProgram(info.materialVariant); - // Bind per-renderable uniform block. There is no need to attempt to skip this command - // because the backends already do this. - size_t const offset = info.hasHybridInstancing ? - 0 : info.index * sizeof(PerRenderableData); - - assert_invariant(info.boh); - - driver.bindBufferRange(BufferObjectBinding::UNIFORM, - +UniformBindingPoints::PER_RENDERABLE, - info.boh, offset, sizeof(PerRenderableUib)); - - if (UTILS_UNLIKELY(info.hasSkinning)) { - - FScene::RenderableSoa const& soa = *mRenderableSoa; - - const FRenderableManager::SkinningBindingInfo& skinning = - soa.elementAt(info.index); - - // note: we can't bind less than sizeof(PerRenderableBoneUib) due to glsl limitations - driver.bindBufferRange(BufferObjectBinding::UNIFORM, - +UniformBindingPoints::PER_RENDERABLE_BONES, - skinning.handle, - skinning.offset * sizeof(PerRenderableBoneUib::BoneData), - sizeof(PerRenderableBoneUib)); - // note: always bind the skinningTexture because the shader needs it. - driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, - skinning.handleSampler); - // note: even if only skinning is enabled, binding morphTargetBuffer is needed. - driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING, - info.morphTargetBuffer); - - // FIXME: Currently we need to rebind the PipelineState when texture or - // UBO binding change. - rebindPipeline = true; - } - - if (UTILS_UNLIKELY(info.hasMorphing)) { - - FScene::RenderableSoa const& soa = *mRenderableSoa; - - const FRenderableManager::SkinningBindingInfo& skinning = - soa.elementAt(info.index); - - const FRenderableManager::MorphingBindingInfo& morphing = - soa.elementAt(info.index); - - // Instead of using a UBO per primitive, we could also have a single UBO for all - // primitives and use bindUniformBufferRange which might be more efficient. - driver.bindUniformBuffer(+UniformBindingPoints::PER_RENDERABLE_MORPHING, - morphing.handle); - driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING, - info.morphTargetBuffer); - // note: even if only morphing is enabled, binding skinningTexture is needed. - driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, - skinning.handleSampler); - - // FIXME: Currently we need to rebind the PipelineState when texture or - // UBO binding change. - rebindPipeline = true; - } - - if (rebindPipeline || - (memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) { - rebindPipeline = false; + if (UTILS_UNLIKELY(memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) { currentPipeline = pipeline; driver.bindPipeline(pipeline); @@ -1036,11 +1025,21 @@ void RenderPass::Executor::execute(FEngine& engine, +PushConstantIds::MORPHING_BUFFER_OFFSET, int32_t(info.morphingOffset)); } - if (info.rph != currentPrimitiveHandle) { + if (UTILS_UNLIKELY(info.rph != currentPrimitiveHandle)) { currentPrimitiveHandle = info.rph; driver.bindRenderPrimitive(info.rph); } + // Bind per-renderable uniform block. There is no need to attempt to skip this command + // because the backends already do this. + uint32_t const offset = info.hasHybridInstancing ? + 0 : info.index * sizeof(PerRenderableData); + + assert_invariant(info.dsh); + driver.bindDescriptorSet(info.dsh, + +DescriptorSetBindingPoints::PER_RENDERABLE, + {{ offset, info.skinningOffset }, driver}); + driver.draw2(info.indexOffset, info.indexCount, info.instanceCount); } } @@ -1055,17 +1054,17 @@ void RenderPass::Executor::execute(FEngine& engine, // ------------------------------------------------------------------------------------------------ -RenderPass::Executor::Executor(RenderPass const* pass, Command const* b, Command const* e, - BufferObjectSharedHandle instancedUbo) noexcept - : mRenderableSoa(&pass->mRenderableSoa), - mCommands(b, e), - mCustomCommands(pass->mCustomCommands.data(), pass->mCustomCommands.size()), - mInstancedUboHandle(std::move(instancedUbo)), - mScissorViewport(pass->mScissorViewport), +RenderPass::Executor::Executor(RenderPass const& pass, Command const* b, Command const* e) noexcept + : mCommands(b, e), + mCustomCommands(pass.mCustomCommands.data(), pass.mCustomCommands.size()), + mInstancedUboHandle(pass.mInstancedUboHandle), + mInstancedDescriptorSetHandle(pass.mInstancedDescriptorSetHandle), + mColorPassDescriptorSet(pass.mColorPassDescriptorSet), + mScissorViewport(pass.mScissorViewport), mPolygonOffsetOverride(false), mScissorOverride(false) { - assert_invariant(b >= pass->begin()); - assert_invariant(e <= pass->end()); + assert_invariant(b >= pass.begin()); + assert_invariant(e <= pass.end()); } RenderPass::Executor::Executor() noexcept diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index e1c7b3c1813..f748c305b32 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -58,6 +58,7 @@ class CommandBufferQueue; class FMaterialInstance; class FRenderPrimitive; class RenderPassBuilder; +class ColorPassDescriptorSet; class RenderPass { public: @@ -244,25 +245,25 @@ class RenderPass { FMaterialInstance const* mi; uint64_t padding; // make this field 64 bits on all platforms }; - backend::RenderPrimitiveHandle rph; // 4 bytes - backend::VertexBufferInfoHandle vbih; // 4 bytes - backend::BufferObjectHandle boh; // 4 bytes - uint32_t indexOffset; // 4 bytes - uint32_t indexCount; // 4 bytes - uint32_t index = 0; // 4 bytes - backend::SamplerGroupHandle morphTargetBuffer; // 4 bytes - uint32_t morphingOffset = 0; // 4 bytes - - backend::RasterState rasterState; // 4 bytes - - uint16_t instanceCount; // 2 bytes [MSb: user] - Variant materialVariant; // 1 byte - backend::PrimitiveType type : 3; // 1 byte 3 bits - bool hasSkinning : 1; // 1 bit - bool hasMorphing : 1; // 1 bit - bool hasHybridInstancing : 1; // 1 bit - - uint32_t rfu[2]; // 16 bytes + backend::RenderPrimitiveHandle rph; // 4 bytes + backend::VertexBufferInfoHandle vbih; // 4 bytes + backend::DescriptorSetHandle dsh; // 4 bytes + uint32_t indexOffset; // 4 bytes + uint32_t indexCount; // 4 bytes + uint32_t index = 0; // 4 bytes + uint32_t skinningOffset = 0; // 4 bytes + uint32_t morphingOffset = 0; // 4 bytes + + backend::RasterState rasterState; // 4 bytes + + uint16_t instanceCount; // 2 bytes [MSb: user] + Variant materialVariant; // 1 byte + backend::PrimitiveType type : 3; // 1 byte 3 bits + bool hasSkinning : 1; // 1 bit + bool hasMorphing : 1; // 1 bit + bool hasHybridInstancing : 1; // 1 bit + + uint32_t rfu[2]; // 16 bytes }; static_assert(sizeof(PrimitiveInfo) == 56); @@ -321,9 +322,19 @@ class RenderPass { void operator()(backend::BufferObjectHandle handle) noexcept; }; + class DescriptorSetHandleDeleter { + std::reference_wrapper driver; + public: + explicit DescriptorSetHandleDeleter(backend::DriverApi& driver) noexcept : driver(driver) { } + void operator()(backend::DescriptorSetHandle handle) noexcept; + }; + using BufferObjectSharedHandle = SharedHandle< backend::HwBufferObject, BufferObjectHandleDeleter>; + using DescriptorSetSharedHandle = SharedHandle< + backend::HwDescriptorSet, DescriptorSetHandleDeleter>; + /* * Executor holds the range of commands to execute for a given pass */ @@ -333,10 +344,11 @@ class RenderPass { friend class RenderPassBuilder; // these fields are constant after creation - FScene::RenderableSoa const* mRenderableSoa = nullptr; utils::Slice mCommands; utils::Slice mCustomCommands; BufferObjectSharedHandle mInstancedUboHandle; + DescriptorSetSharedHandle mInstancedDescriptorSetHandle; + ColorPassDescriptorSet const* mColorPassDescriptorSet = nullptr; backend::Viewport mScissorViewport; backend::Viewport mScissor{}; // value of scissor override @@ -344,8 +356,7 @@ class RenderPass { bool mPolygonOffsetOverride : 1; // whether to override the polygon offset setting bool mScissorOverride : 1; // whether to override the polygon offset setting - Executor(RenderPass const* pass, Command const* b, Command const* e, - BufferObjectSharedHandle instancedUbo) noexcept; + Executor(RenderPass const& pass, Command const* b, Command const* e) noexcept; void execute(FEngine& engine, const Command* first, const Command* last) const noexcept; @@ -354,6 +365,7 @@ class RenderPass { backend::Viewport const& scissor) noexcept; public: + // fixme: needed in ShadowMapManager Executor() noexcept; // can't be copied @@ -382,7 +394,7 @@ class RenderPass { } Executor getExecutor(Command const* b, Command const* e) const { - return { this, b, e, mInstancedUboHandle }; + return { *this, b, e }; } private: @@ -394,8 +406,7 @@ class RenderPass { // the current camera, geometry and flags set. This can be called multiple times if needed. void appendCommands(FEngine& engine, utils::Slice commands, - backend::BufferObjectHandle uboHandle, - utils::Range const visibleRenderables, + utils::Range visibleRenderables, CommandTypeFlags commandTypeFlags, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, @@ -427,16 +438,14 @@ class RenderPass { static inline void generateCommands(CommandTypeFlags commandTypeFlags, Command* commands, FScene::RenderableSoa const& soa, utils::Range range, - backend::BufferObjectHandle renderablesUbo, Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, math::float3 cameraPosition, math::float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept; template - static inline Command* generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags, Command* curr, - FScene::RenderableSoa const& soa, utils::Range range, - backend::BufferObjectHandle renderablesUbo, + static inline RenderPass::Command* generateCommandsImpl(RenderPass::CommandTypeFlags extraFlags, + Command* curr, FScene::RenderableSoa const& soa, utils::Range range, Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, math::float3 cameraPosition, math::float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept; @@ -448,11 +457,12 @@ class RenderPass { FScene::RenderableSoa& renderableData, utils::Range vr) noexcept; FScene::RenderableSoa const& mRenderableSoa; + ColorPassDescriptorSet const* const mColorPassDescriptorSet; backend::Viewport const mScissorViewport; Command* mCommandBegin = nullptr; // Pointer to the first command Command* mCommandEnd = nullptr; // Pointer to one past the last command - // a UBO for instanced primitives - BufferObjectSharedHandle mInstancedUboHandle; + BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives + DescriptorSetSharedHandle mInstancedDescriptorSetHandle; // a descriptor-set to hold the ubo // a vector for our custom commands using CustomCommandVector = std::vector>; @@ -467,11 +477,11 @@ class RenderPassBuilder { backend::Viewport mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX }; FScene::RenderableSoa const* mRenderableSoa = nullptr; utils::Range mVisibleRenderables{}; - backend::Handle mUboHandle; math::float3 mCameraPosition{}; math::float3 mCameraForwardVector{}; RenderPass::RenderFlags mFlags{}; Variant mVariant{}; + ColorPassDescriptorSet const* mColorPassDescriptorSet = nullptr; FScene::VisibleMaskType mVisibilityMask = std::numeric_limits::max(); using CustomCommandRecord = std::tuple< @@ -502,11 +512,10 @@ class RenderPassBuilder { } // specifies the geometry to generate commands for - RenderPassBuilder& geometry(FScene::RenderableSoa const& soa, utils::Range vr, - backend::Handle uboHandle) noexcept { + RenderPassBuilder& geometry( + FScene::RenderableSoa const& soa, utils::Range vr) noexcept { mRenderableSoa = &soa; mVisibleRenderables = vr; - mUboHandle = uboHandle; return *this; } @@ -536,6 +545,12 @@ class RenderPassBuilder { return *this; } + // variant to use + RenderPassBuilder& colorPassDescriptorSet(ColorPassDescriptorSet const* colorPassDescriptorSet) noexcept { + mColorPassDescriptorSet = colorPassDescriptorSet; + return *this; + } + // Sets the visibility mask, which is AND-ed against each Renderable's VISIBLE_MASK to // determine if the renderable is visible for this pass. // Defaults to all 1's, which means all renderables in this render pass will be rendered. diff --git a/filament/src/RenderPrimitive.cpp b/filament/src/RenderPrimitive.cpp index 8bd5e5ad573..42918c0b429 100644 --- a/filament/src/RenderPrimitive.cpp +++ b/filament/src/RenderPrimitive.cpp @@ -33,7 +33,7 @@ namespace filament { void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, - const RenderableManager::Builder::Entry& entry) noexcept { + FRenderableManager::Entry const& entry) noexcept { assert_invariant(entry.materialInstance); diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h index 07d7431f021..a4716399e18 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -41,7 +41,7 @@ class FRenderPrimitive { FRenderPrimitive() noexcept = default; void init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, - const RenderableManager::Builder::Entry& entry) noexcept; + FRenderableManager::Entry const& entry) noexcept; void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, RenderableManager::PrimitiveType type, @@ -56,6 +56,7 @@ class FRenderPrimitive { backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; } uint32_t getIndexOffset() const noexcept { return mIndexOffset; } uint32_t getIndexCount() const noexcept { return mIndexCount; } + uint32_t getMorphingBufferOffset() const noexcept { return mMorphingBufferOffset; } backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; } AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; } @@ -72,6 +73,10 @@ class FRenderPrimitive { mGlobalBlendOrderEnabled = enabled; } + void setMorphingBufferOffset(uint32_t offset) noexcept { + mMorphingBufferOffset = offset; + } + private: // These first fields are dereferences from PrimitiveInfo, keep them together struct { @@ -80,6 +85,7 @@ class FRenderPrimitive { backend::Handle mVertexBufferInfoHandle = {}; uint32_t mIndexOffset = 0; uint32_t mIndexCount = 0; + uint32_t mMorphingBufferOffset = 0; }; AttributeBitset mEnabledAttributes = {}; diff --git a/filament/src/RenderableManager.cpp b/filament/src/RenderableManager.cpp index 9550197acf0..9c98af9d490 100644 --- a/filament/src/RenderableManager.cpp +++ b/filament/src/RenderableManager.cpp @@ -158,20 +158,13 @@ void RenderableManager::setMorphWeights(Instance instance, float const* weights, downcast(this)->setMorphWeights(instance, weights, count, offset); } -void RenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, - MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) { - downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, - downcast(morphTargetBuffer), offset, count); -} - void RenderableManager::setMorphTargetBufferAt( Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count) { downcast(this)->setMorphTargetBufferAt(instance, level, primitiveIndex, offset, count); } -MorphTargetBuffer* RenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level, - size_t primitiveIndex) const noexcept { - return downcast(this)->getMorphTargetBufferAt(instance, level, primitiveIndex); +MorphTargetBuffer* RenderableManager::getMorphTargetBuffer(Instance instance) const noexcept { + return downcast(this)->getMorphTargetBuffer(instance); } size_t RenderableManager::getMorphTargetCount(Instance instance) const noexcept { diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index 2e252633c47..68e6e4729af 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -236,7 +236,7 @@ FrameGraphId RendererUtils::colorPass( view.prepareViewport(static_cast(out.params.viewport), config.logicalViewport); - view.commitUniforms(driver); + view.commitUniformsAndSamplers(driver); // TODO: this should be a parameter of FrameGraphRenderPass::Descriptor out.params.clearStencil = config.clearStencil; @@ -315,7 +315,11 @@ std::pair, bool> RendererUtils::refractionPass( }, config, { .asSubpass = false }, pass.getExecutor(pass.begin(), refraction)); - // generate the mipmap chain + + // Generate the mipmap chain + // Note: we can run some post-processing effects while the "color pass" descriptor set + // in bound because only the descriptor 0 (frame uniforms) matters, and it's + // present in both. PostProcessManager::generateMipmapSSR(ppm, fg, input, ssrConfig.refraction, true, ssrConfig); diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 7015e14466f..60cde28b80f 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -1344,32 +1344,32 @@ math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) void ShadowMap::prepareCamera(Transaction const& transaction, FEngine& engine, const CameraInfo& cameraInfo) noexcept { - PerShadowMapUniforms::prepareCamera(transaction, engine, cameraInfo); - PerShadowMapUniforms::prepareLodBias(transaction, 0.0f); + ShadowMapDescriptorSet::prepareCamera(transaction, engine, cameraInfo); + ShadowMapDescriptorSet::prepareLodBias(transaction, 0.0f); } void ShadowMap::prepareViewport(Transaction const& transaction, backend::Viewport const& viewport) noexcept { - PerShadowMapUniforms::prepareViewport(transaction, viewport); + ShadowMapDescriptorSet::prepareViewport(transaction, viewport); } void ShadowMap::prepareTime(Transaction const& transaction, FEngine& engine, math::float4 const& userTime) noexcept { - PerShadowMapUniforms::prepareTime(transaction, engine, userTime); + ShadowMapDescriptorSet::prepareTime(transaction, engine, userTime); } void ShadowMap::prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept { - PerShadowMapUniforms::prepareShadowMapping(transaction, highPrecision); + ShadowMapDescriptorSet::prepareShadowMapping(transaction, highPrecision); } -PerShadowMapUniforms::Transaction ShadowMap::open(DriverApi& driver) noexcept { - return PerShadowMapUniforms::open(driver); +ShadowMapDescriptorSet::Transaction ShadowMap::open(DriverApi& driver) noexcept { + return ShadowMapDescriptorSet::open(driver); } void ShadowMap::commit(Transaction& transaction, - backend::DriverApi& driver) const noexcept { - mPerShadowMapUniforms.commit(transaction, driver); + FEngine& engine, backend::DriverApi& driver) const noexcept { + mPerShadowMapUniforms.commit(transaction, engine, driver); } void ShadowMap::bind(backend::DriverApi& driver) const noexcept { diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index d0ca26945f9..0d6cbc3427d 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -20,7 +20,7 @@ #include #include "Culler.h" -#include "PerShadowMapUniforms.h" +#include "ds/ShadowMapDescriptorSet.h" #include "details/Camera.h" #include "details/Scene.h" @@ -196,7 +196,7 @@ class ShadowMap { ShadowType getShadowType() const noexcept { return mShadowType; } uint8_t getFace() const noexcept { return mFace; } - using Transaction = PerShadowMapUniforms::Transaction; + using Transaction = ShadowMapDescriptorSet::Transaction; static void prepareCamera(Transaction const& transaction, FEngine& engine, const CameraInfo& cameraInfo) noexcept; @@ -206,9 +206,8 @@ class ShadowMap { FEngine& engine, math::float4 const& userTime) noexcept; static void prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept; - static PerShadowMapUniforms::Transaction open(backend::DriverApi& driver) noexcept; - void commit(Transaction& transaction, - backend::DriverApi& driver) const noexcept; + static ShadowMapDescriptorSet::Transaction open(backend::DriverApi& driver) noexcept; + void commit(Transaction& transaction, FEngine& engine, backend::DriverApi& driver) const noexcept; void bind(backend::DriverApi& driver) const noexcept; private: @@ -346,7 +345,7 @@ class ShadowMap { { 2, 6, 7, 3 }, // top }; - mutable PerShadowMapUniforms mPerShadowMapUniforms; // 4 + mutable ShadowMapDescriptorSet mPerShadowMapUniforms; // 4 FCamera* mCamera = nullptr; // 8 FCamera* mDebugCamera = nullptr; // 8 diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 8943371855c..e48b5203860 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -353,7 +353,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG ShadowMap::prepareTime(transaction, engine, userTime); ShadowMap::prepareShadowMapping(transaction, vsmShadowOptions.highPrecision); - shadowMap.commit(transaction, driver); + shadowMap.commit(transaction, engine, driver); // updatePrimitivesLod must be run before RenderPass::appendCommands. view.updatePrimitivesLod(engine, @@ -364,9 +364,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG RenderPass const pass = passBuilder .camera(cameraInfo) .visibilityMask(entry.visibilityMask) - .geometry(scene->getRenderableData(), - entry.range, - view.getRenderableUBO()) + .geometry(scene->getRenderableData(), entry.range) .commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW) .build(engine); diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 38017f5c8b1..8dee58de143 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -19,7 +19,7 @@ #include "Culler.h" #include "ShadowMap.h" -#include "TypedUniformBuffer.h" +#include "ds/TypedBuffer.h" #include #include @@ -221,7 +221,7 @@ class ShadowMapManager { SoftShadowOptions mSoftShadowOptions; - mutable TypedUniformBuffer mShadowUb; + mutable TypedBuffer mShadowUb; backend::Handle mShadowUbh; ShadowMappingUniforms mShadowMappingUniforms = {}; diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index 596cd8e7fe7..c22e728efca 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -20,6 +20,8 @@ #include "components/RenderableManager.h" +#include "ds/DescriptorSet.h" + #include "details/Engine.h" #include "details/VertexBuffer.h" #include "details/IndexBuffer.h" @@ -69,7 +71,7 @@ namespace filament { using namespace backend; struct RenderableManager::BuilderDetails { - using Entry = RenderableManager::Builder::Entry; + using Entry = FRenderableManager::Entry; std::vector mEntries; Box mAABB; uint8_t mLayerMask = 0x1; @@ -139,7 +141,7 @@ RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index, RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, UTILS_UNUSED size_t minIndex, UTILS_UNUSED size_t maxIndex, size_t count) noexcept { - std::vector& entries = mImpl->mEntries; + std::vector& entries = mImpl->mEntries; if (index < entries.size()) { entries[index].vertices = vertices; entries[index].indices = indices; @@ -282,18 +284,12 @@ RenderableManager::Builder& RenderableManager::Builder::morphing( } RenderableManager::Builder& RenderableManager::Builder::morphing( - uint8_t level, size_t primitiveIndex, size_t offset, size_t count) noexcept { - return morphing(level, primitiveIndex, mImpl->mMorphTargetBuffer, offset, count); -} - -RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t, size_t primitiveIndex, - MorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) noexcept { - std::vector& entries = mImpl->mEntries; + uint8_t level, size_t primitiveIndex, size_t offset, size_t) noexcept { + // the last parameter "count" is unused, because it must be equal to the primitive's vertex count + std::vector& entries = mImpl->mEntries; if (primitiveIndex < entries.size()) { auto& morphing = entries[primitiveIndex].morphing; - morphing.buffer = morphTargetBuffer; - morphing.offset = offset; - morphing.count = count; + morphing.offset = uint32_t(offset); } return *this; } @@ -560,7 +556,7 @@ void FRenderableManager::create( if (ci) { // create and initialize all needed RenderPrimitives using size_type = Slice::size_type; - Builder::Entry const * const entries = builder->mEntries.data(); + auto const * const entries = builder->mEntries.data(); const size_t entryCount = builder->mEntries.size(); FRenderPrimitive* rp = new FRenderPrimitive[entryCount]; auto& factory = mHwRenderPrimitiveFactory; @@ -668,27 +664,20 @@ void FRenderableManager::create( if (morphTargetBuffer == nullptr) { morphTargetBuffer = mEngine.getDummyMorphTargetBuffer(); } - MorphTargets* const morphTargets = new MorphTargets[entryCount]; - std::generate_n(morphTargets, entryCount, - [morphTargetBuffer]() -> MorphTargets { - return { morphTargetBuffer, 0, 0 }; - }); - - mManager[ci].morphTargets = { morphTargets, size_type(entryCount) }; // Always create skinning and morphing resources if one of them is enabled because // the shader always handles both. See Variant::SKINNING_OR_MORPHING. if (UTILS_UNLIKELY(boneCount > 0 || targetCount > 0)) { - auto [sampler, texture] = FSkinningBuffer::createIndicesAndWeightsHandle( - downcast(engine), builder->mBoneIndicesAndWeightsCount); + Bones& bones = manager[ci].bones; + bones.handleTexture = FSkinningBuffer::createIndicesAndWeightsHandle( + engine, builder->mBoneIndicesAndWeightsCount); if (builder->mBoneIndicesAndWeightsCount > 0) { - FSkinningBuffer::setIndicesAndWeightsData(downcast(engine), texture, - builder->mBoneIndicesAndWeights, builder->mBoneIndicesAndWeightsCount); + FSkinningBuffer::setIndicesAndWeightsData(engine, + bones.handleTexture, + builder->mBoneIndicesAndWeights, + builder->mBoneIndicesAndWeightsCount); } - Bones& bones = manager[ci].bones; - bones.handleSamplerGroup = sampler; - bones.handleTexture = texture; // Instead of using a UBO per primitive, we could also have a single UBO for all primitives // and use bindUniformBufferRange which might be more efficient. @@ -700,13 +689,13 @@ void FRenderableManager::create( backend::BufferUsage::DYNAMIC), .count = targetCount }; - for (size_t i = 0; i < entryCount; ++i) { - const auto& morphing = builder->mEntries[i].morphing; - if (!morphing.buffer) { - continue; + Slice& primitives = mManager[ci].primitives; + mManager[ci].morphTargetBuffer = morphTargetBuffer; + if (builder->mMorphTargetBuffer) { + for (size_t i = 0; i < entryCount; ++i) { + const auto& morphing = builder->mEntries[i].morphing; + primitives[i].setMorphingBufferOffset(morphing.offset); } - morphTargets[i] = { downcast(morphing.buffer), (uint32_t)morphing.offset, - (uint32_t)morphing.count }; } // When targetCount equal 0, boneCount>0 in this case, do an initialization for the @@ -762,15 +751,18 @@ void FRenderableManager::destroyComponent(Instance ci) noexcept { // See create(RenderableManager::Builder&, Entity) destroyComponentPrimitives(mHwRenderPrimitiveFactory, driver, manager[ci].primitives); - destroyComponentMorphTargets(engine, manager[ci].morphTargets); + + // destroy the per-renderable descriptor set if we have one + DescriptorSet& descriptorSet = manager[ci].descriptorSet; + descriptorSet.terminate(driver); // destroy the bones structures if any Bones const& bones = manager[ci].bones; if (bones.handle && !bones.skinningBufferMode) { + // when not in skinningBufferMode, we now the handle, so we destroy it driver.destroyBufferObject(bones.handle); } - if (bones.handleSamplerGroup){ - driver.destroySamplerGroup(bones.handleSamplerGroup); + if (bones.handleTexture) { driver.destroyTexture(bones.handleTexture); } @@ -795,11 +787,6 @@ void FRenderableManager::destroyComponentPrimitives( delete[] primitives.data(); } -void FRenderableManager::destroyComponentMorphTargets(FEngine&, - utils::Slice& morphTargets) noexcept { - delete[] morphTargets.data(); -} - void FRenderableManager::setMaterialInstanceAt(Instance instance, uint8_t level, size_t primitiveIndex, FMaterialInstance const* mi) { if (instance) { @@ -969,41 +956,25 @@ void FRenderableManager::setMorphWeights(Instance instance, float const* weights } void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, - size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer, size_t offset, size_t count) { + size_t primitiveIndex, size_t offset, size_t count) { if (instance) { - assert_invariant(morphTargetBuffer); + assert_invariant(mManager[instance].morphTargetBuffer); MorphWeights const& morphWeights = mManager[instance].morphWeights; FILAMENT_CHECK_PRECONDITION(morphWeights.count == count) << "Only " << morphWeights.count << " morph targets can be set (count=" << count << ")"; - Slice& morphTargets = getMorphTargets(instance, level); - if (primitiveIndex < morphTargets.size()) { - morphTargets[primitiveIndex] = { morphTargetBuffer, (uint32_t)offset, - (uint32_t)count }; - } - } -} - -void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, - size_t primitiveIndex, size_t offset, size_t count) { - if (instance) { - Slice& morphTargets = getMorphTargets(instance, level); - if (primitiveIndex < morphTargets.size()) { - setMorphTargetBufferAt(instance, level, - primitiveIndex, morphTargets[primitiveIndex].buffer, offset, count); + Slice& primitives = mManager[instance].primitives; + if (primitiveIndex < primitives.size()) { + primitives[primitiveIndex].setMorphingBufferOffset(offset); } } } -MorphTargetBuffer* FRenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level, - size_t primitiveIndex) const noexcept { +MorphTargetBuffer* FRenderableManager::getMorphTargetBuffer(Instance instance) const noexcept { if (instance) { - const Slice& morphTargets = getMorphTargets(instance, level); - if (primitiveIndex < morphTargets.size()) { - return morphTargets[primitiveIndex].buffer; - } + return mManager[instance].morphTargetBuffer; } return nullptr; } diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 0fa44c83e77..eebb171ef92 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -21,6 +21,8 @@ #include "HwRenderPrimitiveFactory.h" +#include "ds/DescriptorSet.h" + #include
#include @@ -80,12 +82,6 @@ class FRenderableManager : public RenderableManager { static_assert(sizeof(Visibility) == sizeof(uint16_t), "Visibility should be 16 bits"); - struct MorphTargets { - FMorphTargetBuffer* buffer = nullptr; - uint32_t offset = 0; - uint32_t count = 0; - }; - explicit FRenderableManager(FEngine& engine) noexcept; ~FRenderableManager(); @@ -155,11 +151,9 @@ class FRenderableManager : public RenderableManager { 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); void setMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex, size_t offset, size_t count); - MorphTargetBuffer* getMorphTargetBufferAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept; + MorphTargetBuffer* getMorphTargetBuffer(Instance instance) const noexcept; size_t getMorphTargetCount(Instance instance) const noexcept; void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; @@ -176,11 +170,12 @@ class FRenderableManager : public RenderableManager { inline uint8_t getLayerMask(Instance instance) const noexcept; inline uint8_t getPriority(Instance instance) const noexcept; inline uint8_t getChannels(Instance instance) const noexcept; + inline DescriptorSet& getDescriptorSet(Instance instance) noexcept; struct SkinningBindingInfo { backend::Handle handle; uint32_t offset; - backend::Handle handleSampler; + backend::Handle boneIndicesAndWeightHandle; }; inline SkinningBindingInfo getSkinningBufferInfo(Instance instance) const noexcept; @@ -189,7 +184,7 @@ class FRenderableManager : public RenderableManager { struct MorphingBindingInfo { backend::Handle handle; uint32_t count; - MorphTargets const* targets; // Pointer to Slice at a renderable. + FMorphTargetBuffer const* morphTargetBuffer; }; inline MorphingBindingInfo getMorphingBufferInfo(Instance instance) const noexcept; @@ -218,26 +213,35 @@ class FRenderableManager : public RenderableManager { AttributeBitset getEnabledAttributesAt(Instance instance, uint8_t level, size_t primitiveIndex) const noexcept; inline utils::Slice const& getRenderPrimitives(Instance instance, uint8_t level) const noexcept; inline utils::Slice& getRenderPrimitives(Instance instance, uint8_t level) noexcept; - inline utils::Slice const& getMorphTargets(Instance instance, uint8_t level) const noexcept; - inline utils::Slice& getMorphTargets(Instance instance, uint8_t level) noexcept; + + struct Entry { + VertexBuffer* vertices = nullptr; + IndexBuffer* indices = nullptr; + uint32_t offset = 0; + uint32_t count = 0; + MaterialInstance const* materialInstance = nullptr; + PrimitiveType type = PrimitiveType::TRIANGLES; + uint16_t blendOrder = 0; + bool globalBlendOrderEnabled = false; + struct { + uint32_t offset = 0; + } morphing; + }; private: void destroyComponent(Instance ci) noexcept; static void destroyComponentPrimitives( HwRenderPrimitiveFactory& factory, backend::DriverApi& driver, utils::Slice& primitives) noexcept; - static void destroyComponentMorphTargets(FEngine& engine, - utils::Slice& morphTargets) noexcept; struct Bones { backend::Handle handle; + backend::Handle handleTexture; uint16_t count = 0; uint16_t offset = 0; - bool skinningBufferMode = false; - backend::Handle handleSamplerGroup; - backend::Handle handleTexture; + bool skinningBufferMode = false; // whether we own (false) handle or not (true) }; - static_assert(sizeof(Bones) == 20); + static_assert(sizeof(Bones) == 16); struct MorphWeights { backend::Handle handle; @@ -254,7 +258,8 @@ class FRenderableManager : public RenderableManager { VISIBILITY, // user data PRIMITIVES, // user data BONES, // filament data, UBO storing a pointer to the bones information - MORPH_TARGETS + MORPHTARGET_BUFFER, // morphtarget buffer for the component + DESCRIPTOR_SET // per-renderable descriptor set }; using Base = utils::SingleInstanceComponentManager< @@ -266,7 +271,8 @@ class FRenderableManager : public RenderableManager { Visibility, // VISIBILITY utils::Slice, // PRIMITIVES Bones, // BONES - utils::Slice // MORPH_TARGETS + FMorphTargetBuffer*, // MORPHTARGET_BUFFER + filament::DescriptorSet // DESCRIPTOR_SET >; struct Sim : public Base { @@ -289,7 +295,8 @@ class FRenderableManager : public RenderableManager { Field visibility; Field primitives; Field bones; - Field morphTargets; + Field morphTargetBuffer; + Field descriptorSet; }; }; @@ -450,7 +457,7 @@ Box const& FRenderableManager::getAABB(Instance instance) const noexcept { FRenderableManager::SkinningBindingInfo FRenderableManager::getSkinningBufferInfo(Instance instance) const noexcept { Bones const& bones = mManager[instance].bones; - return { bones.handle, bones.offset, bones.handleSamplerGroup }; + return { bones.handle, bones.offset, bones.handleTexture }; } inline uint32_t FRenderableManager::getBoneCount(Instance instance) const noexcept { @@ -461,8 +468,8 @@ inline uint32_t FRenderableManager::getBoneCount(Instance instance) const noexce FRenderableManager::MorphingBindingInfo FRenderableManager::getMorphingBufferInfo(Instance instance) const noexcept { MorphWeights const& morphWeights = mManager[instance].morphWeights; - utils::Slice const& morphTargets = getMorphTargets(instance, 0); - return { morphWeights.handle, morphWeights.count, morphTargets.data() }; + FMorphTargetBuffer const* const buffer = mManager[instance].morphTargetBuffer; + return { morphWeights.handle, morphWeights.count, buffer }; } FRenderableManager::InstancesInfo @@ -480,14 +487,8 @@ utils::Slice& FRenderableManager::getRenderPrimitives( return mManager[instance].primitives; } -utils::Slice const& FRenderableManager::getMorphTargets( - Instance instance, UTILS_UNUSED uint8_t level) const noexcept { - return mManager[instance].morphTargets; -} - -utils::Slice& FRenderableManager::getMorphTargets( - Instance instance, UTILS_UNUSED uint8_t level) noexcept { - return mManager[instance].morphTargets; +DescriptorSet& FRenderableManager::getDescriptorSet(Instance instance) noexcept { + return mManager[instance].descriptorSet; } } // namespace filament diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 2547b40103d..7af0b6e22b5 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -38,6 +38,8 @@ #include +#include + #include #include @@ -341,6 +343,19 @@ void FEngine::init() { driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1, { zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE }); + + mPerViewDescriptorSetLayoutSsrVariant = { + driverApi, + descriptor_sets::getSsrVariantLayout() }; + + mPerViewDescriptorSetLayoutDepthVariant = { + driverApi, + descriptor_sets::getDepthVariantLayout() }; + + mPerRenderableDescriptorSetLayout = { + driverApi, + descriptor_sets::getPerRenderableLayout() }; + #ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0 if (UTILS_UNLIKELY(mActiveFeatureLevel == FeatureLevel::FEATURE_LEVEL_0)) { FMaterial::DefaultMaterialBuilder defaultMaterialBuilder; @@ -465,7 +480,12 @@ void FEngine::shutdown() { mLightManager.terminate(); // free-up all lights mCameraManager.terminate(*this); // free-up all cameras + mPerViewDescriptorSetLayoutDepthVariant.terminate(driver); + mPerViewDescriptorSetLayoutSsrVariant.terminate(driver); + mPerRenderableDescriptorSetLayout.terminate(driver); + driver.destroyRenderPrimitive(mFullScreenTriangleRph); + destroy(mFullScreenTriangleIb); destroy(mFullScreenTriangleVb); destroy(mDummyMorphTargetBuffer); diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index b8cdb498773..880fe630a27 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -30,6 +30,8 @@ #include "components/TransformManager.h" #include "components/RenderableManager.h" +#include "ds/DescriptorSetLayout.h" + #include "details/BufferObject.h" #include "details/Camera.h" #include "details/ColorGrading.h" @@ -423,6 +425,18 @@ class FEngine : public Engine { return mHwVertexBufferInfoFactory; } + DescriptorSetLayout const& getPerViewDescriptorSetLayoutDepthVariant() const noexcept { + return mPerViewDescriptorSetLayoutDepthVariant; + } + + DescriptorSetLayout const& getPerViewDescriptorSetLayoutSsrVariant() const noexcept { + return mPerViewDescriptorSetLayoutSsrVariant; + } + + DescriptorSetLayout const& getPerRenderableDescriptorSetLayout() const noexcept { + return mPerRenderableDescriptorSetLayout; + } + backend::Handle getOneTexture() const { return mDummyOneTexture; } backend::Handle getZeroTexture() const { return mDummyZeroTexture; } backend::Handle getOneTextureArray() const { return mDummyOneTextureArray; } @@ -492,6 +506,9 @@ class FEngine : public Engine { FCameraManager mCameraManager; ResourceAllocator* mResourceAllocator = nullptr; HwVertexBufferInfoFactory mHwVertexBufferInfoFactory; + DescriptorSetLayout mPerViewDescriptorSetLayoutDepthVariant; + DescriptorSetLayout mPerViewDescriptorSetLayoutSsrVariant; + DescriptorSetLayout mPerRenderableDescriptorSetLayout; ResourceList mBufferObjects{ "BufferObject" }; ResourceList mRenderers{ "Renderer" }; diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index b8fa8d2999b..268af1ded0b 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -20,9 +20,12 @@ #include "Froxelizer.h" #include "MaterialParser.h" +#include "ds/ColorPassDescriptorSet.h" + #include "FilamentAPI-impl.h" #include +#include #include #include #include @@ -230,21 +233,13 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder, assert_invariant(success); if (UTILS_UNLIKELY(parser->getShaderLanguage() == ShaderLanguage::ESSL1)) { - success = parser->getBindingUniformInfo(&mBindingUniformInfo); - assert_invariant(success); - success = parser->getAttributeInfo(&mAttributeInfo); assert_invariant(success); - } else if (mFeatureLevel <= FeatureLevel::FEATURE_LEVEL_1) { - // this chunk is not needed for materials at feature level 2 and above - success = parser->getUniformBlockBindings(&mUniformBlockBindings); + + success = parser->getBindingUniformInfo(&mBindingUniformInfo); assert_invariant(success); } - success = parser->getSamplerBlockBindings( - &mSamplerGroupBindingInfoList, &mSamplerBindingToNameMap); - assert_invariant(success); - // Older materials will not have a subpass chunk; this should not be an error. if (!parser->getSubpasses(&mSubpassInfo)) { mSubpassInfo.isValid = false; @@ -301,10 +296,16 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder, processSpecializationConstants(engine, builder, parser); processPushConstants(engine, parser); processDepthVariants(engine, parser); + processDescriptorSets(engine, parser); // we can only initialize the default instance once we're initialized ourselves new(&mDefaultInstanceStorage) FMaterialInstance(engine, this); + mPerViewLayoutIndex = ColorPassDescriptorSet::getIndex( + mIsVariantLit, + mReflectionMode == ReflectionMode::SCREEN_SPACE || + mRefractionMode == RefractionMode::SCREEN_SPACE, + !(mVariantFilterMask & +UserVariantFilterBit::FOG)); #if FILAMENT_ENABLE_MATDBG // Register the material with matdbg. @@ -378,6 +379,9 @@ void FMaterial::terminate(FEngine& engine) { destroyPrograms(engine); getDefaultInstance()->terminate(engine); + + mPerViewDescriptorSetLayout.terminate(engine.getDriverApi()); + mDescriptorSetLayout.terminate(engine.getDriverApi()); } void FMaterial::compile(CompilerPriorityQueue priority, @@ -548,35 +552,23 @@ Program FMaterial::getProgramWithVariants( program.shader(ShaderStage::VERTEX, vsBuilder.data(), vsBuilder.size()) .shader(ShaderStage::FRAGMENT, fsBuilder.data(), fsBuilder.size()) .shaderLanguage(mMaterialParser->getShaderLanguage()) - .uniformBlockBindings(mUniformBlockBindings) .diagnostics(mName, [this, variant](io::ostream& out) -> io::ostream& { return out << mName.c_str_safe() << ", variant=(" << io::hex << variant.key << io::dec << ")"; }); - UTILS_NOUNROLL - for (size_t i = 0; i < Enum::count(); i++) { - SamplerBindingPoints const bindingPoint = (SamplerBindingPoints)i; - auto const& info = mSamplerGroupBindingInfoList[i]; - if (info.count) { - std::array samplers{}; - for (size_t j = 0, c = info.count; j < c; ++j) { - uint8_t const binding = info.bindingOffset + j; - samplers[j] = { mSamplerBindingToNameMap[binding], binding }; - } - program.setSamplerGroup(+bindingPoint, info.shaderStageFlags, - samplers.data(), info.count); - } - } if (UTILS_UNLIKELY(mMaterialParser->getShaderLanguage() == ShaderLanguage::ESSL1)) { assert_invariant(!mBindingUniformInfo.empty()); - for (auto const& [index, uniforms] : mBindingUniformInfo) { - program.uniforms(uint32_t(index), uniforms); + for (auto const& [index, name, uniforms] : mBindingUniformInfo) { + program.uniforms(uint32_t(index), name, uniforms); } program.attributes(mAttributeInfo); } + program.descriptorBindings(0, mProgramDescriptorBindings[0]); + program.descriptorBindings(1, mProgramDescriptorBindings[1]); + program.descriptorBindings(2, mProgramDescriptorBindings[2]); program.specializationConstants(mSpecializationConstants); program.pushConstants(ShaderStage::VERTEX, mPushConstants[(uint8_t) ShaderStage::VERTEX]); @@ -1005,6 +997,26 @@ void FMaterial::processDepthVariants(FEngine& engine, MaterialParser const* cons } } +void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* const parser) { + UTILS_UNUSED_IN_RELEASE bool success; + + success = parser->getDescriptorBindings(&mProgramDescriptorBindings); + assert_invariant(success); + + std::array descriptorSetLayout; + success = parser->getDescriptorSetLayout(&descriptorSetLayout); + assert_invariant(success); + + mDescriptorSetLayout = { engine.getDriverApi(), std::move(descriptorSetLayout[0]) }; + + mPerViewDescriptorSetLayout = { engine.getDriverApi(), std::move(descriptorSetLayout[1]) }; +} + +backend::descriptor_binding_t FMaterial::getSamplerBinding( + std::string_view const& name) const { + return mSamplerInterfaceBlock.getSamplerInfo(name)->binding; +} + template bool FMaterial::setConstant(uint32_t id, int32_t value) noexcept; template bool FMaterial::setConstant(uint32_t id, float value) noexcept; template bool FMaterial::setConstant(uint32_t id, bool value) noexcept; diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 617f9699501..5ef8f709aef 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -21,12 +21,13 @@ #include "details/MaterialInstance.h" +#include "ds/DescriptorSetLayout.h" + #include #include #include #include -#include #include #include #include @@ -45,12 +46,13 @@ #include #include -#include #include #include #include #include #include +#include +#include #include #include @@ -86,9 +88,24 @@ class FMaterial : public Material { return mUniformInterfaceBlock; } - // return the uniform interface block for this material - const SamplerInterfaceBlock& getSamplerInterfaceBlock() const noexcept { - return mSamplerInterfaceBlock; + DescriptorSetLayout const& getPerViewDescriptorSetLayout() const noexcept { + assert_invariant(mMaterialDomain == MaterialDomain::POST_PROCESS); + return mPerViewDescriptorSetLayout; + } + + DescriptorSetLayout const& getPerViewDescriptorSetLayout(Variant variant) const noexcept { + assert_invariant(mMaterialDomain == MaterialDomain::SURFACE); + if (Variant::isValidDepthVariant(variant)) { + return mEngine.getPerViewDescriptorSetLayoutDepthVariant(); + } + if (Variant::isSSRVariant(variant)) { + return mEngine.getPerViewDescriptorSetLayoutSsrVariant(); + } + return mPerViewDescriptorSetLayout; + } + + DescriptorSetLayout const& getDescriptorSetLayout() const noexcept { + return mDescriptorSetLayout; } void compile(CompilerPriorityQueue priority, @@ -182,10 +199,17 @@ class FMaterial : public Material { float getSpecularAntiAliasingVariance() const noexcept { return mSpecularAntiAliasingVariance; } float getSpecularAntiAliasingThreshold() const noexcept { return mSpecularAntiAliasingThreshold; } + backend::descriptor_binding_t getSamplerBinding( + std::string_view const& name) const; + bool hasMaterialProperty(Property property) const noexcept { return bool(mMaterialProperties & uint64_t(property)); } + SamplerInterfaceBlock const& getSamplerInterfaceBlock() const noexcept { + return mSamplerInterfaceBlock; + } + size_t getParameterCount() const noexcept { return mUniformInterfaceBlock.getFieldInfoList().size() + mSamplerInterfaceBlock.getSamplerInfoList().size() + @@ -205,6 +229,10 @@ class FMaterial : public Material { template> bool setConstant(uint32_t id, T value) noexcept; + uint8_t getPerViewLayoutIndex() const noexcept { + return mPerViewLayoutIndex; + } + #if FILAMENT_ENABLE_MATDBG void applyPendingEdits() noexcept; @@ -257,10 +285,15 @@ class FMaterial : public Material { void processDepthVariants(FEngine& engine, MaterialParser const* parser); + void processDescriptorSets(FEngine& engine, MaterialParser const* parser); + void createAndCacheProgram(backend::Program&& p, Variant variant) const noexcept; // try to order by frequency of use mutable std::array, VARIANT_COUNT> mCachedPrograms; + DescriptorSetLayout mPerViewDescriptorSetLayout; + DescriptorSetLayout mDescriptorSetLayout; + backend::Program::DescriptorSetInfo mProgramDescriptorBindings; backend::RasterState mRasterState; TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT; @@ -280,6 +313,7 @@ class FMaterial : public Material { RefractionType mRefractionType = RefractionType::SOLID; ReflectionMode mReflectionMode = ReflectionMode::DEFAULT; uint64_t mMaterialProperties = 0; + uint8_t mPerViewLayoutIndex = 0; float mMaskThreshold = 0.4f; float mSpecularAntiAliasingVariance = 0.0f; @@ -294,16 +328,15 @@ class FMaterial : public Material { // reserve some space to construct the default material instance std::aligned_storage::type mDefaultInstanceStorage; - static_assert(sizeof(mDefaultInstanceStorage) >= sizeof(mDefaultInstanceStorage)); + static_assert(sizeof(mDefaultInstanceStorage) >= sizeof(FMaterialInstance)); SamplerInterfaceBlock mSamplerInterfaceBlock; BufferInterfaceBlock mUniformInterfaceBlock; SubpassInfo mSubpassInfo; - utils::FixedCapacityVector> mUniformBlockBindings; utils::FixedCapacityVector mDepthVariants; // only populated with default material - using BindingUniformInfoContainer = utils::FixedCapacityVector< - std::pair>; + using BindingUniformInfoContainer = utils::FixedCapacityVector>; BindingUniformInfoContainer mBindingUniformInfo; @@ -311,8 +344,6 @@ class FMaterial : public Material { AttributeInfoContainer mAttributeInfo; - SamplerGroupBindingInfoList mSamplerGroupBindingInfoList; - SamplerBindingToNameMap mSamplerBindingToNameMap; // Constants defined by this Material utils::FixedCapacityVector mMaterialConstants; // A map from the Constant name to the mMaterialConstant index diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index 4595c7e6c3b..a9ae242d4a9 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -16,18 +16,35 @@ #include -#include - -#include "details/MaterialInstance.h" - #include "RenderPass.h" +#include "ds/DescriptorSetLayout.h" + #include "details/Engine.h" #include "details/Material.h" +#include "details/MaterialInstance.h" #include "details/Texture.h" +#include "private/filament/EngineEnums.h" + +#include + +#include +#include + +#include +#include +#include +#include #include +#include + +#include +#include +#include +#include + using namespace filament::math; using namespace utils; @@ -37,6 +54,7 @@ using namespace backend; FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept : mMaterial(material), + mDescriptorSet(material->getDescriptorSetLayout()), mCulling(CullingMode::BACK), mDepthFunc(RasterState::DepthFunc::LE), mColorWrite(false), @@ -53,11 +71,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) BufferObjectBinding::UNIFORM, backend::BufferUsage::STATIC); } - if (!material->getSamplerInterfaceBlock().isEmpty()) { - mSamplers = SamplerGroup(material->getSamplerInterfaceBlock().getSize()); - mSbHandle = driver.createSamplerGroup( - mSamplers.getSize(), utils::FixedSizeString<32>(mMaterial->getName().c_str_safe())); - } + // set the UBO, always descriptor 0 + mDescriptorSet.setBuffer(0, mUbHandle, 0, mUniforms.getSize()); const RasterState& rasterState = material->getRasterState(); // At the moment, only MaterialInstances have a stencil state, but in the future it should be @@ -95,6 +110,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name) : mMaterial(other->mMaterial), + mDescriptorSet(other->mMaterial->getDescriptorSetLayout()), mPolygonOffset(other->mPolygonOffset), mStencilState(other->mStencilState), mMaskThreshold(other->mMaskThreshold), @@ -118,11 +134,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, BufferObjectBinding::UNIFORM, backend::BufferUsage::DYNAMIC); } - if (!material->getSamplerInterfaceBlock().isEmpty()) { - mSamplers = other->getSamplerGroup(); - mSbHandle = driver.createSamplerGroup( - mSamplers.getSize(), utils::FixedSizeString<32>(mMaterial->getName().c_str_safe())); - } + // set the UBO, always descriptor 0 + mDescriptorSet.setBuffer(0, mUbHandle, 0, mUniforms.getSize()); if (material->hasDoubleSidedCapability()) { setDoubleSided(mIsDoubleSided); @@ -154,26 +167,25 @@ FMaterialInstance::~FMaterialInstance() noexcept = default; void FMaterialInstance::terminate(FEngine& engine) { FEngine::DriverApi& driver = engine.getDriverApi(); + mDescriptorSet.terminate(driver); driver.destroyBufferObject(mUbHandle); - driver.destroySamplerGroup(mSbHandle); } -void FMaterialInstance::commitSlow(DriverApi& driver) const { +void FMaterialInstance::commit(DriverApi& driver) const { // update uniforms if needed if (mUniforms.isDirty()) { driver.updateBufferObject(mUbHandle, mUniforms.toBufferDescriptor(driver), 0); } - if (mSamplers.isDirty()) { - driver.updateSamplerGroup(mSbHandle, mSamplers.toBufferDescriptor(driver)); - } + // Commit descriptors if needed (e.g. when textures are updated,or the first time) + mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver); } // ------------------------------------------------------------------------------------------------ void FMaterialInstance::setParameter(std::string_view name, - backend::Handle texture, backend::SamplerParams params) noexcept { - size_t const index = mMaterial->getSamplerInterfaceBlock().getSamplerInfo(name)->offset; - mSamplers.setSampler(index, { texture, params }); + backend::Handle texture, backend::SamplerParams params) { + auto binding = mMaterial->getSamplerBinding(name); + mDescriptorSet.setSampler(binding, texture, params); } void FMaterialInstance::setParameterImpl(std::string_view name, @@ -271,4 +283,76 @@ const char* FMaterialInstance::getName() const noexcept { return mName.c_str(); } +// ------------------------------------------------------------------------------------------------ + +void FMaterialInstance::use(FEngine::DriverApi& driver) const { + + // Here we check that all declared sampler parameters are set, this is required by + // Vulkan and Metal; GL is more permissive. If a sampler parameter is not set, we will + // log a warning once per MaterialInstance in the system log and patch-in a dummy + // texture. + + auto const& layout = mMaterial->getDescriptorSetLayout(); + auto const samplersDescriptors = layout.getSamplerDescriptors(); + auto const validDescriptors = mDescriptorSet.getValidDescriptors(); + auto const missingSamplerDescriptors = + (validDescriptors & samplersDescriptors) ^ samplersDescriptors; + + if (UTILS_UNLIKELY(missingSamplerDescriptors.any())) { + auto const& list = mMaterial->getSamplerInterfaceBlock().getSamplerInfoList(); + std::call_once(mMissingSamplersFlag, [this, missingSamplerDescriptors, &list]() { + + slog.w << "sampler parameters not set in MaterialInstance \"" + << mName.c_str_safe() << "\" or Material \"" + << mMaterial->getName().c_str_safe() << "\":\n"; + + missingSamplerDescriptors.forEachSetBit([&list](descriptor_binding_t binding) { + auto pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) { + return item.binding == binding; + }); + // just safety-check, should never fail + if (UTILS_LIKELY(pos != list.end())) { + slog.w << "[" << +binding << "] " << pos->name.c_str() << '\n'; + } + }); + flush(slog.w); + }); + + // here we need to set the samplers that are missing + missingSamplerDescriptors.forEachSetBit([this, &list](descriptor_binding_t binding) { + auto pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) { + return item.binding == binding; + }); + + FEngine const& engine = mMaterial->getEngine(); + + // just safety-check, should never fail + if (UTILS_LIKELY(pos != list.end())) { + switch (pos->type) { + case SamplerType::SAMPLER_2D: + mDescriptorSet.setSampler(binding, + engine.getZeroTexture(), {}); + break; + case SamplerType::SAMPLER_2D_ARRAY: + mDescriptorSet.setSampler(binding, + engine.getZeroTextureArray(), {}); + break; + case SamplerType::SAMPLER_CUBEMAP: + mDescriptorSet.setSampler(binding, + engine.getDummyCubemap()->getHwHandle(), {}); + break; + case SamplerType::SAMPLER_EXTERNAL: + case SamplerType::SAMPLER_3D: + case SamplerType::SAMPLER_CUBEMAP_ARRAY: + // we're currently not able to fix-up those + break; + } + } + }); + mDescriptorSet.commit(layout, driver); + } + + mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL); +} + } // namespace filament diff --git a/filament/src/details/MaterialInstance.h b/filament/src/details/MaterialInstance.h index fc1d9a35f04..985555b42e7 100644 --- a/filament/src/details/MaterialInstance.h +++ b/filament/src/details/MaterialInstance.h @@ -18,19 +18,29 @@ #define TNT_FILAMENT_DETAILS_MATERIALINSTANCE_H #include "downcast.h" + #include "UniformBuffer.h" + +#include "ds/DescriptorSet.h" + #include "details/Engine.h" #include "private/backend/DriverApi.h" -#include +#include -#include +#include #include -#include +#include -#include +#include +#include +#include +#include + +#include +#include namespace filament { @@ -48,27 +58,15 @@ class FMaterialInstance : public MaterialInstance { void terminate(FEngine& engine); - void commit(FEngine::DriverApi& driver) const { - if (UTILS_UNLIKELY(mUniforms.isDirty() || mSamplers.isDirty())) { - commitSlow(driver); - } - } + void commit(FEngine::DriverApi& driver) const; - void use(FEngine::DriverApi& driver) const { - if (mUbHandle) { - driver.bindUniformBuffer(+UniformBindingPoints::PER_MATERIAL_INSTANCE, mUbHandle); - } - if (mSbHandle) { - driver.bindSamplers(+SamplerBindingPoints::PER_MATERIAL_INSTANCE, mSbHandle); - } - } + void use(FEngine::DriverApi& driver) const; FMaterial const* getMaterial() const noexcept { return mMaterial; } uint64_t getSortingKey() const noexcept { return mMaterialSortingKey; } UniformBuffer const& getUniformBuffer() const noexcept { return mUniforms; } - backend::SamplerGroup const& getSamplerGroup() const noexcept { return mSamplers; } void setScissor(uint32_t left, uint32_t bottom, uint32_t width, uint32_t height) noexcept { constexpr uint32_t maxvalu = std::numeric_limits::max(); @@ -208,7 +206,7 @@ class FMaterialInstance : public MaterialInstance { const char* getName() const noexcept; void setParameter(std::string_view name, - backend::Handle texture, backend::SamplerParams params) noexcept; + backend::Handle texture, backend::SamplerParams params); using MaterialInstance::setParameter; @@ -237,15 +235,12 @@ class FMaterialInstance : public MaterialInstance { // initialize the default instance FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept; - void commitSlow(FEngine::DriverApi& driver) const; - // keep these grouped, they're accessed together in the render-loop FMaterial const* mMaterial = nullptr; backend::Handle mUbHandle; - backend::Handle mSbHandle; + mutable filament::DescriptorSet mDescriptorSet; UniformBuffer mUniforms; - backend::SamplerGroup mSamplers; backend::PolygonOffset mPolygonOffset{}; backend::StencilState mStencilState{}; @@ -271,6 +266,7 @@ class FMaterialInstance : public MaterialInstance { }; utils::CString mName; + mutable std::once_flag mMissingSamplersFlag; }; FILAMENT_DOWNCAST(MaterialInstance) diff --git a/filament/src/details/MorphTargetBuffer.cpp b/filament/src/details/MorphTargetBuffer.cpp index d4124fb2a6b..71aa13ab7eb 100644 --- a/filament/src/details/MorphTargetBuffer.cpp +++ b/filament/src/details/MorphTargetBuffer.cpp @@ -123,25 +123,14 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder) getHeight(mVertexCount), mCount, TextureUsage::DEFAULT); - - // create and update sampler group - mSbHandle = driver.createSamplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT, - utils::FixedSizeString<32>("Morph target samplers")); - SamplerGroup samplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT); - samplerGroup.setSampler(PerRenderPrimitiveMorphingSib::POSITIONS, { mPbHandle, {}}); - samplerGroup.setSampler(PerRenderPrimitiveMorphingSib::TANGENTS, { mTbHandle, {}}); - driver.updateSamplerGroup(mSbHandle, samplerGroup.toBufferDescriptor(driver)); } void FMorphTargetBuffer::terminate(FEngine& engine) { FEngine::DriverApi& driver = engine.getDriverApi(); - if (UTILS_LIKELY(mSbHandle)) { - driver.destroySamplerGroup(mSbHandle); - } - if (UTILS_LIKELY(mTbHandle)) { + if (mTbHandle) { driver.destroyTexture(mTbHandle); } - if (UTILS_LIKELY(mPbHandle)) { + if (mPbHandle) { driver.destroyTexture(mPbHandle); } } diff --git a/filament/src/details/MorphTargetBuffer.h b/filament/src/details/MorphTargetBuffer.h index 50e8fc92b92..6e42dcace88 100644 --- a/filament/src/details/MorphTargetBuffer.h +++ b/filament/src/details/MorphTargetBuffer.h @@ -21,15 +21,15 @@ #include -#include "backend/DriverApiForward.h" - -#include "private/backend/SamplerGroup.h" - +#include +#include #include -#include +#include +#include -#include +#include +#include namespace filament { @@ -59,21 +59,23 @@ class FMorphTargetBuffer : public MorphTargetBuffer { inline size_t getVertexCount() const noexcept { return mVertexCount; } inline size_t getCount() const noexcept { return mCount; } -private: - friend class FView; - friend class RenderPass; + backend::TextureHandle getPositionsHandle() const noexcept { + return mPbHandle; + } + backend::TextureHandle getTangentsHandle() const noexcept { + return mTbHandle; + } + +private: void updateDataAt(backend::DriverApi& driver, backend::Handle handle, backend::PixelDataFormat format, backend::PixelDataType type, const char* out, size_t elementSize, size_t targetIndex, size_t count, size_t offset); - inline backend::Handle getHwHandle() const noexcept { return mSbHandle; } - - backend::Handle mSbHandle; - backend::Handle mPbHandle; - backend::Handle mTbHandle; - size_t mVertexCount; - size_t mCount; + backend::TextureHandle mPbHandle; + backend::TextureHandle mTbHandle; + uint32_t mVertexCount; + uint32_t mCount; }; FILAMENT_DOWNCAST(MorphTargetBuffer) diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 15094a07591..125f28d5afc 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -528,6 +528,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { JobSystem& js = engine.getJobSystem(); FEngine::DriverApi& driver = engine.getDriverApi(); PostProcessManager& ppm = engine.getPostProcessManager(); + ppm.setFrameUniforms(view.getFrameUniforms()); // DEBUG: driver commands must all happen from the same thread. Enforce that on debug builds. driver.debugThreading(); @@ -839,9 +840,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { scene.getRenderableData(), view.getVisibleRenderables()); passBuilder.camera(cameraInfo); - passBuilder.geometry(scene.getRenderableData(), - view.getVisibleRenderables(), - view.getRenderableUBO()); + passBuilder.geometry(scene.getRenderableData(), view.getVisibleRenderables()); // view set-ups that need to happen before rendering fg.addTrivialSideEffectPass("Prepare View Uniforms", @@ -875,10 +874,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { uint32_t(float(xvp.width ) * aoOptions.resolution), uint32_t(float(xvp.height) * aoOptions.resolution)}); - view.commitUniforms(driver); - - // set uniforms and samplers for the color passes - view.bindPerViewUniformsAndSamplers(driver); + view.commitUniformsAndSamplers(driver); }); // -------------------------------------------------------------------------------------------- @@ -919,8 +915,15 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // Store this frame's camera projection in the frame history. if (UTILS_UNLIKELY(taaOptions.enabled)) { // Apply the TAA jitter to everything after the structure pass, starting with the color pass. - ppm.prepareTaa(fg, svp, taaOptions, view.getFrameHistory(), &FrameHistoryEntry::taa, - &cameraInfo, view.getPerViewUniforms()); + ppm.TaaJitterCamera(svp, taaOptions, view.getFrameHistory(), + &FrameHistoryEntry::taa, &cameraInfo); + + fg.addTrivialSideEffectPass("Jitter Camera", + [&engine, &cameraInfo, &descriptorSet = view.getColorPassDescriptorSet()] + (DriverApi& driver) { + descriptorSet.prepareCamera(engine, cameraInfo); + descriptorSet.commit(driver); + }); } // -------------------------------------------------------------------------------------------- @@ -948,7 +951,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { if (ssReflectionsOptions.enabled) { auto reflections = ppm.ssr(fg, passBuilder, view.getFrameHistory(), cameraInfo, - view.getPerViewUniforms(), structure, ssReflectionsOptions, { .width = svp.width, .height = svp.height }); @@ -974,6 +976,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // (i.e. it won't be culled, unless everything is culled), so no need to complexify things. passBuilder.variant(variant); + passBuilder.colorPassDescriptorSet(&view.getColorPassDescriptorSet()); + // color-grading as subpass is done either by the color pass or the TAA pass if any auto colorGradingConfigForColor = colorGradingConfig; colorGradingConfigForColor.asSubpass = colorGradingConfigForColor.asSubpass && !taaOptions.enabled; @@ -1088,11 +1092,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { } FrameGraphId input = colorPassOutput; - fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) { - // Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass. - view.cleanupRenderPasses(); - view.commitUniforms(driver); - }); // Resolve depth -- which might be needed because of TAA or DoF. This pass will be culled // if the depth is not used below or if the depth is not MS (e.g. it could have been diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 9690e6386e2..03898570e2b 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -22,6 +22,8 @@ #include "Allocators.h" #include "Culler.h" +#include "ds/DescriptorSet.h" + #include "components/LightManager.h" #include "components/RenderableManager.h" #include "components/TransformManager.h" @@ -105,6 +107,7 @@ class FScene : public Scene { PRIMITIVES, // 8 | level-of-detail'ed primitives SUMMED_PRIMITIVE_COUNT, // 4 | summed visible primitive counts UBO, // 128 | + DESCRIPTOR_SET_HANDLE, // FIXME: We need a better way to handle this USER_DATA, // 4 | user data currently used to store the scale @@ -125,6 +128,7 @@ class FScene : public Scene { utils::Slice, // PRIMITIVES uint32_t, // SUMMED_PRIMITIVE_COUNT PerRenderableData, // UBO + backend::DescriptorSetHandle, // DESCRIPTOR_SET_HANDLE // FIXME: We need a better way to handle this float // USER_DATA >; diff --git a/filament/src/details/SkinningBuffer.cpp b/filament/src/details/SkinningBuffer.cpp index b7092638670..5970d56c942 100644 --- a/filament/src/details/SkinningBuffer.cpp +++ b/filament/src/details/SkinningBuffer.cpp @@ -18,8 +18,6 @@ #include "components/RenderableManager.h" -#include "private/filament/SibStructs.h" - #include "details/Engine.h" #include "FilamentAPI-impl.h" @@ -27,7 +25,10 @@ #include #include -#include +#include + +#include +#include namespace filament { @@ -226,29 +227,15 @@ void updateDataAt(backend::DriverApi& driver, } } -FSkinningBuffer::HandleIndicesAndWeights FSkinningBuffer::createIndicesAndWeightsHandle( +backend::TextureHandle FSkinningBuffer::createIndicesAndWeightsHandle( FEngine& engine, size_t count) { - backend::Handle samplerHandle; - backend::Handle textureHandle; - FEngine::DriverApi& driver = engine.getDriverApi(); // create a texture for skinning pairs data (bone index and weight) - textureHandle = driver.createTexture(SamplerType::SAMPLER_2D, 1, + return driver.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RG32F, 1, getSkinningBufferWidth(count), getSkinningBufferHeight(count), 1, TextureUsage::DEFAULT); - samplerHandle = driver.createSamplerGroup(PerRenderPrimitiveSkinningSib::SAMPLER_COUNT, - utils::FixedSizeString<32>("Skinning buffer samplers")); - SamplerGroup samplerGroup(PerRenderPrimitiveSkinningSib::SAMPLER_COUNT); - samplerGroup.setSampler(PerRenderPrimitiveSkinningSib::BONE_INDICES_AND_WEIGHTS, - { textureHandle, {}}); - driver.updateSamplerGroup(samplerHandle, - samplerGroup.toBufferDescriptor(driver)); - return { - .sampler = samplerHandle, - .texture = textureHandle - }; } void FSkinningBuffer::setIndicesAndWeightsData(FEngine& engine, diff --git a/filament/src/details/SkinningBuffer.h b/filament/src/details/SkinningBuffer.h index 8fa056a5796..fd831ef122e 100644 --- a/filament/src/details/SkinningBuffer.h +++ b/filament/src/details/SkinningBuffer.h @@ -17,19 +17,24 @@ #ifndef TNT_FILAMENT_DETAILS_SKINNINGBUFFER_H #define TNT_FILAMENT_DETAILS_SKINNINGBUFFER_H -#include "downcast.h" #include -#include "private/filament/EngineEnums.h" -#include "private/filament/UibStructs.h" +#include "downcast.h" + +#include +#include #include - #include -#include +#include + +#include #include +#include +#include + // for gtest class FilamentTest_Bones_Test; @@ -55,9 +60,6 @@ class FSkinningBuffer : public SkinningBuffer { return (count + CONFIG_MAX_BONE_COUNT - 1) & ~(CONFIG_MAX_BONE_COUNT - 1); } - backend::Handle setIndicesAndWeights(FEngine& engine, - math::float2 const* pairs, size_t count); - private: friend class ::FilamentTest_Bones_Test; friend class SkinningBuffer; @@ -75,12 +77,8 @@ class FSkinningBuffer : public SkinningBuffer { return mHandle; } - struct HandleIndicesAndWeights{ - backend::Handle sampler; - backend::Handle texture; - }; - static HandleIndicesAndWeights createIndicesAndWeightsHandle(FEngine& engine, - size_t count); + static backend::TextureHandle createIndicesAndWeightsHandle(FEngine& engine, size_t count); + static void setIndicesAndWeightsData(FEngine& engine, backend::Handle textureHandle, const utils::FixedCapacityVector& pairs, diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index ed9daeb4b06..f3d12686abc 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -58,10 +59,12 @@ static constexpr float PID_CONTROLLER_Ki = 0.002f; static constexpr float PID_CONTROLLER_Kd = 0.0f; FView::FView(FEngine& engine) - : mFroxelizer(engine), + : mCommonRenderableDescriptorSet(engine.getPerRenderableDescriptorSetLayout()), + mFroxelizer(engine), mFogEntity(engine.getEntityManager().create()), mIsStereoSupported(engine.getDriverApi().isStereoSupported()), - mPerViewUniforms(engine) { + mUniforms(engine.getDriverApi()), + mColorPassDescriptorSet(engine, mUniforms) { DriverApi& driver = engine.getDriverApi(); @@ -101,6 +104,11 @@ FView::FView(FEngine& engine) mIsDynamicResolutionSupported = driver.isFrameTimeSupported(); mDefaultColorGrading = mColorGrading = engine.getDefaultColorGrading(); + + mColorPassDescriptorSet.init( + mLightUbh, + mFroxelizer.getRecordBuffer(), + mFroxelizer.getFroxelBuffer()); } FView::~FView() noexcept = default; @@ -121,8 +129,10 @@ void FView::terminate(FEngine& engine) { drainFrameHistory(engine); ShadowMapManager::terminate(engine, mShadowMapManager); - mPerViewUniforms.terminate(driver); + mUniforms.terminate(driver); + mColorPassDescriptorSet.terminate(driver); mFroxelizer.terminate(driver); + mCommonRenderableDescriptorSet.terminate(driver); engine.getEntityManager().destroy(mFogEntity); } @@ -377,7 +387,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc */ const float exposure = Exposure::exposure(cameraInfo.ev100); - mPerViewUniforms.prepareExposure(cameraInfo.ev100); + mColorPassDescriptorSet.prepareExposure(cameraInfo.ev100); /* * Indirect light (IBL) @@ -394,7 +404,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc FSkybox const* const skybox = scene->getSkybox(); intensity = skybox ? skybox->getIntensity() : FIndirectLight::DEFAULT_INTENSITY; } - mPerViewUniforms.prepareAmbientLight(engine, *ibl, intensity, exposure); + mColorPassDescriptorSet.prepareAmbientLight(engine, *ibl, intensity, exposure); /* * Directional light (always at index 0) @@ -402,7 +412,7 @@ void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexc FLightManager::Instance const directionalLight = lightData.elementAt(0); const float3 sceneSpaceDirection = lightData.elementAt(0); // guaranteed normalized - mPerViewUniforms.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight); + mColorPassDescriptorSet.prepareDirectionalLight(engine, exposure, sceneSpaceDirection, directionalLight); } CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { @@ -554,7 +564,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) { // TODO: might be more consistent to do this in prepareLighting(), but it's not // strictly necessary - mPerViewUniforms.prepareDynamicLights(mFroxelizer); + mColorPassDescriptorSet.prepareDynamicLights(mFroxelizer); } // We need to pass viewMatrix by value here because it extends the scope of this // function. @@ -657,6 +667,67 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren } assert_invariant(mRenderableUbh); scene->updateUBOs(merged, mRenderableUbh); + + mCommonRenderableDescriptorSet.setBuffer( + +PerRenderableBindingPoints::OBJECT_UNIFORMS, mRenderableUbh, + 0, sizeof(PerRenderableUib)); + + mCommonRenderableDescriptorSet.commit( + engine.getPerRenderableDescriptorSetLayout(), driver); + } + } + + { // this must happen after mRenderableUbh is created/updated + // prepare skinning, morphing and hybrid instancing + auto& sceneData = scene->getRenderableData(); + for (uint32_t const i : merged) { + auto const& skinning = sceneData.elementAt(i); + auto const& morphing = sceneData.elementAt(i); + auto const& instance = sceneData.elementAt(i); + + // FIXME: when only one is active the UBO handle of the other is null + // (probably a problem on vulkan) + if (UTILS_UNLIKELY(skinning.handle || morphing.handle || instance.handle)) { + auto const ci = sceneData.elementAt(i); + FRenderableManager& rcm = engine.getRenderableManager(); + auto& descriptorSet = rcm.getDescriptorSet(ci); + + // initialize the descriptor set the first time it's needed + if (UTILS_UNLIKELY(!descriptorSet.getHandle())) { + descriptorSet = DescriptorSet{ engine.getPerRenderableDescriptorSetLayout() }; + } + + descriptorSet.setBuffer(+PerRenderableBindingPoints::OBJECT_UNIFORMS, + instance.handle ? instance.handle : mRenderableUbh, + 0, sizeof(PerRenderableUib)); + + if (UTILS_UNLIKELY(skinning.handle || morphing.handle)) { + + descriptorSet.setBuffer(+PerRenderableBindingPoints::BONES_UNIFORMS, + skinning.handle, 0, sizeof(PerRenderableBoneUib)); + + descriptorSet.setSampler(+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, + skinning.boneIndicesAndWeightHandle, {}); + + descriptorSet.setBuffer(+PerRenderableBindingPoints::MORPHING_UNIFORMS, + morphing.handle, 0, sizeof(PerRenderableMorphingUib)); + + descriptorSet.setSampler(+PerRenderableBindingPoints::MORPH_TARGET_POSITIONS, + morphing.morphTargetBuffer->getPositionsHandle(), {}); + + descriptorSet.setSampler(+PerRenderableBindingPoints::MORPH_TARGET_TANGENTS, + morphing.morphTargetBuffer->getTangentsHandle(), {}); + } + + descriptorSet.commit(engine.getPerRenderableDescriptorSetLayout(), driver); + + // write the descriptor-set handle to the sceneData array for access later + sceneData.elementAt(i) = descriptorSet.getHandle(); + } else { + // use the shared descriptor-set + sceneData.elementAt(i) = + mCommonRenderableDescriptorSet.getHandle(); + } } } @@ -675,35 +746,12 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren auto const& tcm = engine.getTransformManager(); auto const fogTransform = tcm.getWorldTransformAccurate(tcm.getInstance(mFogEntity)); - mPerViewUniforms.prepareTime(engine, userTime); - mPerViewUniforms.prepareFog(engine, cameraInfo, fogTransform, mFogOptions, + mColorPassDescriptorSet.prepareTime(engine, userTime); + mColorPassDescriptorSet.prepareFog(engine, cameraInfo, fogTransform, mFogOptions, scene->getIndirectLight()); - mPerViewUniforms.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions); - mPerViewUniforms.prepareBlending(needsAlphaChannel); - mPerViewUniforms.prepareMaterialGlobals(mMaterialGlobals); -} - -void FView::bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept { - mPerViewUniforms.bind(driver); - - if (UTILS_UNLIKELY(driver.getFeatureLevel() == backend::FeatureLevel::FEATURE_LEVEL_0)) { - return; - } - - driver.bindUniformBuffer(+UniformBindingPoints::LIGHTS, - mLightUbh); - - if (needsShadowMap()) { - assert_invariant(mShadowMapManager->getShadowUniformsHandle()); - driver.bindUniformBuffer(+UniformBindingPoints::SHADOW, - mShadowMapManager->getShadowUniformsHandle()); - } - - driver.bindUniformBuffer(+UniformBindingPoints::FROXEL_RECORDS, - mFroxelizer.getRecordBuffer()); - - driver.bindUniformBuffer(+UniformBindingPoints::FROXELS, - mFroxelizer.getFroxelBuffer()); + mColorPassDescriptorSet.prepareTemporalNoise(engine, mTemporalAntiAliasingOptions); + mColorPassDescriptorSet.prepareBlending(needsAlphaChannel); + mColorPassDescriptorSet.prepareMaterialGlobals(mMaterialGlobals); } void FView::computeVisibilityMasks( @@ -764,12 +812,12 @@ void FView::prepareUpscaler(float2 scale, derivativesScale = 0.5f; } } - mPerViewUniforms.prepareLodBias(bias, derivativesScale); + mColorPassDescriptorSet.prepareLodBias(bias, derivativesScale); } void FView::prepareCamera(FEngine& engine, const CameraInfo& cameraInfo) const noexcept { SYSTRACE_CALL(); - mPerViewUniforms.prepareCamera(engine, cameraInfo); + mColorPassDescriptorSet.prepareCamera(engine, cameraInfo); } void FView::prepareViewport( @@ -778,23 +826,23 @@ void FView::prepareViewport( SYSTRACE_CALL(); // TODO: we should pass viewport.{left|bottom} to the backend, so it can offset the // scissor properly. - mPerViewUniforms.prepareViewport(physicalViewport, logicalViewport); + mColorPassDescriptorSet.prepareViewport(physicalViewport, logicalViewport); } void FView::prepareSSAO(Handle ssao) const noexcept { - mPerViewUniforms.prepareSSAO(ssao, mAmbientOcclusionOptions); + mColorPassDescriptorSet.prepareSSAO(ssao, mAmbientOcclusionOptions); } void FView::prepareSSR(Handle ssr, bool disableSSR, float refractionLodOffset, ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept { - mPerViewUniforms.prepareSSR(ssr, disableSSR, refractionLodOffset, ssrOptions); + mColorPassDescriptorSet.prepareSSR(ssr, disableSSR, refractionLodOffset, ssrOptions); } void FView::prepareStructure(Handle structure) const noexcept { // sampler must be NEAREST - mPerViewUniforms.prepareStructure(structure); + mColorPassDescriptorSet.prepareStructure(structure); } void FView::prepareShadow(Handle texture) const noexcept { @@ -806,33 +854,33 @@ void FView::prepareShadow(Handle texture) const noexcept { } switch (mShadowType) { case filament::ShadowType::PCF: - mPerViewUniforms.prepareShadowPCF(texture, uniforms); + mColorPassDescriptorSet.prepareShadowPCF(texture, uniforms); break; case filament::ShadowType::VSM: - mPerViewUniforms.prepareShadowVSM(texture, uniforms, mVsmShadowOptions); + mColorPassDescriptorSet.prepareShadowVSM(texture, uniforms, mVsmShadowOptions); break; case filament::ShadowType::DPCF: - mPerViewUniforms.prepareShadowDPCF(texture, uniforms, mSoftShadowOptions); + mColorPassDescriptorSet.prepareShadowDPCF(texture, uniforms, mSoftShadowOptions); break; case filament::ShadowType::PCSS: - mPerViewUniforms.prepareShadowPCSS(texture, uniforms, mSoftShadowOptions); + mColorPassDescriptorSet.prepareShadowPCSS(texture, uniforms, mSoftShadowOptions); break; case filament::ShadowType::PCFd: - mPerViewUniforms.prepareShadowPCFDebug(texture, uniforms); + mColorPassDescriptorSet.prepareShadowPCFDebug(texture, uniforms); break; } } void FView::prepareShadowMapping(bool highPrecision) const noexcept { - mPerViewUniforms.prepareShadowMapping(highPrecision); -} - -void FView::cleanupRenderPasses() const noexcept { - mPerViewUniforms.unbindSamplers(); + if (mHasShadowing) { + assert_invariant(mShadowMapManager); + mColorPassDescriptorSet.prepareShadowMapping( + mShadowMapManager->getShadowUniformsHandle(), highPrecision); + } } -void FView::commitUniforms(DriverApi& driver) const noexcept { - mPerViewUniforms.commit(driver); +void FView::commitUniformsAndSamplers(DriverApi& driver) const noexcept { + mColorPassDescriptorSet.commit(driver); } void FView::commitFroxels(DriverApi& driverApi) const noexcept { diff --git a/filament/src/details/View.h b/filament/src/details/View.h index c2fb5bb58b5..9a8037f8758 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -17,21 +17,24 @@ #ifndef TNT_FILAMENT_DETAILS_VIEW_H #define TNT_FILAMENT_DETAILS_VIEW_H -#include - -#include - #include "downcast.h" #include "Allocators.h" +#include "Culler.h" #include "FrameHistory.h" #include "FrameInfo.h" #include "Froxelizer.h" -#include "PerViewUniforms.h" #include "PIDController.h" -#include "ShadowMap.h" #include "ShadowMapManager.h" -#include "TypedUniformBuffer.h" + +#include "ds/ColorPassDescriptorSet.h" +#include "ds/DescriptorSet.h" +#include "ds/PostProcessDescriptorSet.h" +#include "ds/SsrPassDescriptorSet.h" +#include "ds/TypedUniformBuffer.h" + +#include "components/LightManager.h" +#include "components/RenderableManager.h" #include "details/Camera.h" #include "details/ColorGrading.h" @@ -39,13 +42,20 @@ #include "details/Scene.h" #include +#include -#include "private/backend/DriverApi.h" +#include +#include +#include +#include + +#include #include #include #include +#include #include #include #include @@ -55,6 +65,10 @@ #include #include +#include + +#include +#include namespace utils { class JobSystem; @@ -95,8 +109,6 @@ class FView : public View { filament::Viewport viewport, CameraInfo cameraInfo, math::float4 const& userTime, bool needsAlphaChannel) noexcept; - void bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noexcept; - void setScene(FScene* scene) { mScene = scene; } FScene const* getScene() const noexcept { return mScene; } FScene* getScene() noexcept { return mScene; } @@ -157,9 +169,8 @@ class FView : public View { void prepareShadow(backend::Handle structure) const noexcept; void prepareShadowMapping(bool highPrecision) const noexcept; - void cleanupRenderPasses() const noexcept; - void commitUniforms(backend::DriverApi& driver) const noexcept; void commitFroxels(backend::DriverApi& driverApi) const noexcept; + void commitUniformsAndSamplers(backend::DriverApi& driver) const noexcept; utils::JobSystem::Job* getFroxelizerSync() const noexcept { return mFroxelizerSync; } void setFroxelizerSync(utils::JobSystem::Job* sync) noexcept { mFroxelizerSync = sync; } @@ -394,7 +405,7 @@ class FView : public View { } backend::Handle getRenderTargetHandle() const noexcept { - backend::Handle kEmptyHandle; + backend::Handle const kEmptyHandle; return mRenderTarget == nullptr ? kEmptyHandle : mRenderTarget->getHwHandle(); } @@ -409,8 +420,7 @@ class FView : public View { static void cullRenderables(utils::JobSystem& js, FScene::RenderableSoa& renderableData, Frustum const& frustum, size_t bit) noexcept; - PerViewUniforms const& getPerViewUniforms() const noexcept { return mPerViewUniforms; } - PerViewUniforms& getPerViewUniforms() noexcept { return mPerViewUniforms; } + ColorPassDescriptorSet& getColorPassDescriptorSet() noexcept { return mColorPassDescriptorSet; } // Returns the frame history FIFO. This is typically used by the FrameGraph to access // previous frame data. @@ -437,8 +447,8 @@ class FView : public View { return mFogEntity; } - backend::Handle getRenderableUBO() const noexcept { - return mRenderableUbh; + TypedUniformBuffer& getFrameUniforms() noexcept { + return mUniforms; } private: @@ -453,7 +463,7 @@ class FView : public View { // TODO: use a small pool static FPickingQuery* get(uint32_t x, uint32_t y, backend::CallbackHandler* handler, View::PickingQueryResultCallback callback) noexcept { - return new FPickingQuery(x, y, handler, callback); + return new(std::nothrow) FPickingQuery(x, y, handler, callback); } static void put(FPickingQuery* pQuery) noexcept { delete pQuery; @@ -499,10 +509,11 @@ class FView : public View { // these are accessed in the render loop, keep together backend::Handle mLightUbh; backend::Handle mRenderableUbh; + filament::DescriptorSet mCommonRenderableDescriptorSet; FScene* mScene = nullptr; // The camera set by the user, used for culling and viewing - FCamera* /* UTILS_NONNULL */ mCullingCamera = nullptr; // FIXME: should alaways be non-null + FCamera* /* UTILS_NONNULL */ mCullingCamera = nullptr; // FIXME: should always be non-null // The optional (debug) camera, used only for viewing FCamera* mViewingCamera = nullptr; @@ -548,7 +559,8 @@ class FView : public View { RenderQuality mRenderQuality; - mutable PerViewUniforms mPerViewUniforms; + mutable TypedUniformBuffer mUniforms; + mutable ColorPassDescriptorSet mColorPassDescriptorSet; mutable FrameHistory mFrameHistory{}; diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/ds/ColorPassDescriptorSet.cpp similarity index 66% rename from filament/src/PerViewUniforms.cpp rename to filament/src/ds/ColorPassDescriptorSet.cpp index eaf8df8cb46..326db28563d 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/ds/ColorPassDescriptorSet.cpp @@ -14,54 +14,139 @@ * limitations under the License. */ -#include "PerViewUniforms.h" +#include "ColorPassDescriptorSet.h" -#include "DFG.h" #include "Froxelizer.h" #include "ShadowMapManager.h" +#include "TypedUniformBuffer.h" + +#include "components/LightManager.h" #include "details/Camera.h" #include "details/Engine.h" #include "details/IndirectLight.h" #include "details/Texture.h" +#include #include #include #include +#include +#include #include -#include +#include +#include + +#include +#include #include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include namespace filament { using namespace backend; using namespace math; -PerViewUniforms::PerViewUniforms(FEngine& engine) noexcept - : mSamplers(PerViewSib::SAMPLER_COUNT) { - DriverApi& driver = engine.getDriverApi(); +uint8_t ColorPassDescriptorSet::getIndex( + bool lit, bool ssr, bool fog) noexcept { + + uint8_t index = 0; + + if (!lit) { + // this will remove samplers unused when unit + index |= 0x1; + } + + if (ssr) { + // this will add samplers needed for screen-space SSR + index |= 0x2; + } + + if (!fog) { + // this will remove samplers needed for fog + index |= 0x4; + } + + assert_invariant(index < DESCRIPTOR_LAYOUT_COUNT); + return index; +} - mSamplerGroupHandle = driver.createSamplerGroup( - mSamplers.getSize(), utils::FixedSizeString<32>("Per-view samplers")); - mUniformBufferHandle = driver.createBufferObject(mUniforms.getSize(), - BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC); +ColorPassDescriptorSet::ColorPassDescriptorSet(FEngine& engine, + TypedUniformBuffer& uniforms) noexcept + : mUniforms(uniforms) { + + constexpr UserVariantFilterMask filterFog = UserVariantFilterMask(UserVariantFilterBit::FOG); + constexpr UserVariantFilterMask keepFog = UserVariantFilterMask(0); + + for (bool const lit: { false, true }) { + for (bool const ssr: { false, true }) { + for (bool const fog: { false, true }) { + auto index = ColorPassDescriptorSet::getIndex(lit, ssr, fog); + mDescriptorSetLayout[index] = { + engine.getDriverApi(), + descriptor_sets::getPerViewDescriptorSetLayout( + MaterialDomain::SURFACE, + fog ? keepFog : filterFog, + lit, + ssr ? ReflectionMode::SCREEN_SPACE : ReflectionMode::DEFAULT, + ssr ? RefractionMode::SCREEN_SPACE : RefractionMode::NONE) + }; + mDescriptorSet[index] = DescriptorSet{ mDescriptorSetLayout[index] }; + } + } + } + + setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS, + uniforms.getUboHandle(), 0, uniforms.getSize()); if (engine.getDFG().isValid()) { TextureSampler const sampler(TextureSampler::MagFilter::LINEAR); - mSamplers.setSampler(PerViewSib::IBL_DFG_LUT, - { engine.getDFG().getTexture(), sampler.getSamplerParams() }); + setSampler(+PerViewBindingPoints::IBL_DFG_LUT, + engine.getDFG().getTexture(), sampler.getSamplerParams()); + } +} + +void ColorPassDescriptorSet::init( + BufferObjectHandle lights, + BufferObjectHandle recordBuffer, + BufferObjectHandle froxelBuffer) noexcept { + for (auto&& descriptorSet: mDescriptorSet) { + descriptorSet.setBuffer(+PerViewBindingPoints::LIGHTS, + lights, 0, sizeof(LightsUib)); + descriptorSet.setBuffer(+PerViewBindingPoints::RECORD_BUFFER, + recordBuffer, 0, sizeof(FroxelRecordUib)); + descriptorSet.setBuffer(+PerViewBindingPoints::FROXEL_BUFFER, + froxelBuffer, 0, sizeof(FroxelsUib)); } } -void PerViewUniforms::terminate(DriverApi& driver) { - driver.destroyBufferObject(mUniformBufferHandle); - driver.destroySamplerGroup(mSamplerGroupHandle); +void ColorPassDescriptorSet::terminate(DriverApi& driver) { + for (auto&& entry : mDescriptorSet) { + entry.terminate(driver); + } + for (auto&& entry : mDescriptorSetLayout) { + entry.terminate(driver); + } } -void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept { +void ColorPassDescriptorSet::prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept { mat4f const& viewFromWorld = camera.view; mat4f const& worldFromView = camera.model; mat4f const& clipFromView = camera.projection; @@ -96,20 +181,20 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n s.clipControl = engine.getDriverApi().getClipSpaceParams(); } -void PerViewUniforms::prepareLodBias(float bias, float2 derivativesScale) noexcept { +void ColorPassDescriptorSet::prepareLodBias(float bias, float2 derivativesScale) noexcept { auto& s = mUniforms.edit(); s.lodBias = bias; s.derivativesScale = derivativesScale; } -void PerViewUniforms::prepareExposure(float ev100) noexcept { +void ColorPassDescriptorSet::prepareExposure(float ev100) noexcept { const float exposure = Exposure::exposure(ev100); auto& s = mUniforms.edit(); s.exposure = exposure; s.ev100 = ev100; } -void PerViewUniforms::prepareViewport( +void ColorPassDescriptorSet::prepareViewport( const filament::Viewport& physicalViewport, const filament::Viewport& logicalViewport) noexcept { float4 const physical{ physicalViewport.left, physicalViewport.bottom, @@ -122,7 +207,7 @@ void PerViewUniforms::prepareViewport( s.logicalViewportOffset = -logical.xy / logical.zw; } -void PerViewUniforms::prepareTime(FEngine& engine, math::float4 const& userTime) noexcept { +void ColorPassDescriptorSet::prepareTime(FEngine& engine, math::float4 const& userTime) noexcept { auto& s = mUniforms.edit(); const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1000000000; const float fraction = float(double(oneSecondRemainder) / 1000000000.0); @@ -130,7 +215,7 @@ void PerViewUniforms::prepareTime(FEngine& engine, math::float4 const& userTime) s.userTime = userTime; } -void PerViewUniforms::prepareTemporalNoise(FEngine& engine, +void ColorPassDescriptorSet::prepareTemporalNoise(FEngine& engine, TemporalAntiAliasingOptions const& options) noexcept { std::uniform_real_distribution uniformDistribution{ 0.0f, 1.0f }; auto& s = mUniforms.edit(); @@ -138,7 +223,7 @@ void PerViewUniforms::prepareTemporalNoise(FEngine& engine, s.temporalNoise = options.enabled ? temporalNoise : 0.0f; } -void PerViewUniforms::prepareFog(FEngine& engine, const CameraInfo& cameraInfo, +void ColorPassDescriptorSet::prepareFog(FEngine& engine, const CameraInfo& cameraInfo, mat4 const& userWorldFromFog, FogOptions const& options, FIndirectLight const* ibl) noexcept { auto packHalf2x16 = [](math::half2 v) -> uint32_t { @@ -196,11 +281,11 @@ void PerViewUniforms::prepareFog(FEngine& engine, const CameraInfo& cameraInfo, } } - mSamplers.setSampler(PerViewSib::FOG, { + setSampler(+PerViewBindingPoints::FOG, fogColorTextureHandle ? fogColorTextureHandle : engine.getDummyCubemap()->getHwHandle(), { .filterMag = SamplerMagFilter::LINEAR, .filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR - }}); + }); s.fogStart = options.distance; s.fogMaxOpacity = options.maximumOpacity; @@ -214,7 +299,7 @@ void PerViewUniforms::prepareFog(FEngine& engine, const CameraInfo& cameraInfo, s.fogFromWorldMatrix = mat3f{ cof(fogFromWorld) }; } -void PerViewUniforms::prepareSSAO(Handle ssao, +void ColorPassDescriptorSet::prepareSSAO(Handle ssao, AmbientOcclusionOptions const& options) noexcept { // High quality sampling is enabled only if AO itself is enabled and upsampling quality is at // least set to high and of course only if upsampling is needed. @@ -222,10 +307,10 @@ void PerViewUniforms::prepareSSAO(Handle ssao, && options.resolution < 1.0f; // LINEAR filtering is only needed when AO is enabled and low-quality upsampling is used. - mSamplers.setSampler(PerViewSib::SSAO, { ssao, { + setSampler(+PerViewBindingPoints::SSAO, ssao, { .filterMag = options.enabled && !highQualitySampling ? SamplerMagFilter::LINEAR : SamplerMagFilter::NEAREST - }}); + }); const float edgeDistance = 1.0f / options.bilateralThreshold; auto& s = mUniforms.edit(); @@ -234,11 +319,11 @@ void PerViewUniforms::prepareSSAO(Handle ssao, s.aoBentNormals = options.enabled && options.bentNormals ? 1.0f : 0.0f; } -void PerViewUniforms::prepareBlending(bool needsAlphaChannel) noexcept { +void ColorPassDescriptorSet::prepareBlending(bool needsAlphaChannel) noexcept { mUniforms.edit().needsAlphaChannel = needsAlphaChannel ? 1.0f : 0.0f; } -void PerViewUniforms::prepareMaterialGlobals( +void ColorPassDescriptorSet::prepareMaterialGlobals( std::array const& materialGlobals) noexcept { mUniforms.edit().custom[0] = materialGlobals[0]; mUniforms.edit().custom[1] = materialGlobals[1]; @@ -246,30 +331,30 @@ void PerViewUniforms::prepareMaterialGlobals( mUniforms.edit().custom[3] = materialGlobals[3]; } -void PerViewUniforms::prepareSSR(Handle ssr, +void ColorPassDescriptorSet::prepareSSR(Handle ssr, bool disableSSR, float refractionLodOffset, ScreenSpaceReflectionsOptions const& ssrOptions) noexcept { - mSamplers.setSampler(PerViewSib::SSR, { ssr, { + setSampler(+PerViewBindingPoints::SSR, ssr, { .filterMag = SamplerMagFilter::LINEAR, .filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR - }}); + }); auto& s = mUniforms.edit(); s.refractionLodOffset = refractionLodOffset; s.ssrDistance = (ssrOptions.enabled && !disableSSR) ? ssrOptions.maxDistance : 0.0f; } -void PerViewUniforms::prepareHistorySSR(Handle ssr, +void ColorPassDescriptorSet::prepareHistorySSR(Handle ssr, math::mat4f const& historyProjection, math::mat4f const& uvFromViewMatrix, ScreenSpaceReflectionsOptions const& ssrOptions) noexcept { - mSamplers.setSampler(PerViewSib::SSR, { ssr, { + setSampler(+PerViewBindingPoints::SSR, ssr, { .filterMag = SamplerMagFilter::LINEAR, .filterMin = SamplerMinFilter::LINEAR - }}); + }); auto& s = mUniforms.edit(); s.ssrReprojection = historyProjection; @@ -280,20 +365,19 @@ void PerViewUniforms::prepareHistorySSR(Handle ssr, s.ssrStride = ssrOptions.stride; } -void PerViewUniforms::prepareStructure(Handle structure) noexcept { +void ColorPassDescriptorSet::prepareStructure(Handle structure) noexcept { // sampler must be NEAREST - mSamplers.setSampler(PerViewSib::STRUCTURE, { structure, {}}); + setSampler(+PerViewBindingPoints::STRUCTURE, structure, {}); } -void PerViewUniforms::prepareDirectionalLight(FEngine& engine, +void ColorPassDescriptorSet::prepareDirectionalLight(FEngine& engine, float exposure, float3 const& sceneSpaceDirection, - PerViewUniforms::LightManagerInstance directionalLight) noexcept { + ColorPassDescriptorSet::LightManagerInstance directionalLight) noexcept { FLightManager const& lcm = engine.getLightManager(); auto& s = mUniforms.edit(); float const shadowFar = lcm.getShadowFar(directionalLight); - // TODO: make the falloff rate a parameter s.shadowFarAttenuationParams = shadowFar > 0.0f ? 0.5f * float2{ 10.0f, 10.0f / (shadowFar * shadowFar) } : float2{ 1.0f, 0.0f }; @@ -328,12 +412,12 @@ void PerViewUniforms::prepareDirectionalLight(FEngine& engine, } } -void PerViewUniforms::prepareAmbientLight(FEngine& engine, FIndirectLight const& ibl, +void ColorPassDescriptorSet::prepareAmbientLight(FEngine& engine, FIndirectLight const& ibl, float intensity, float exposure) noexcept { auto& s = mUniforms.edit(); // Set up uniforms and sampler for the IBL, guaranteed to be non-null at this point. - float const iblRoughnessOneLevel = ibl.getLevelCount() - 1.0f; + float const iblRoughnessOneLevel = float(ibl.getLevelCount() - 1); s.iblRoughnessOneLevel = iblRoughnessOneLevel; s.iblLuminance = intensity * exposure; std::transform(ibl.getSH(), ibl.getSH() + 9, s.iblSH, [](float3 v) { @@ -345,14 +429,14 @@ void PerViewUniforms::prepareAmbientLight(FEngine& engine, FIndirectLight const& if (!reflection) { reflection = engine.getDummyCubemap()->getHwHandle(); } - mSamplers.setSampler(PerViewSib::IBL_SPECULAR, { + setSampler(+PerViewBindingPoints::IBL_SPECULAR, reflection, { .filterMag = SamplerMagFilter::LINEAR, .filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR - }}); + }); } -void PerViewUniforms::prepareDynamicLights(Froxelizer& froxelizer) noexcept { +void ColorPassDescriptorSet::prepareDynamicLights(Froxelizer& froxelizer) noexcept { auto& s = mUniforms.edit(); froxelizer.updateUniforms(s); float const f = froxelizer.getLightFar(); @@ -360,22 +444,23 @@ void PerViewUniforms::prepareDynamicLights(Froxelizer& froxelizer) noexcept { s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) }; } -void PerViewUniforms::prepareShadowMapping(bool highPrecision) noexcept { +void ColorPassDescriptorSet::prepareShadowMapping(backend::BufferObjectHandle shadowUniforms, bool highPrecision) noexcept { auto& s = mUniforms.edit(); constexpr float low = 5.54f; // ~ std::log(std::numeric_limits::max()) * 0.5f; constexpr float high = 42.0f; // ~ std::log(std::numeric_limits::max()) * 0.5f; s.vsmExponent = highPrecision ? high : low; + setBuffer(+PerViewBindingPoints::SHADOWS, shadowUniforms, 0, sizeof(ShadowUib)); } -void PerViewUniforms::prepareShadowSampling(PerViewUib& uniforms, +void ColorPassDescriptorSet::prepareShadowSampling(PerViewUib& uniforms, ShadowMappingUniforms const& shadowMappingUniforms) noexcept { uniforms.cascadeSplits = shadowMappingUniforms.cascadeSplits; uniforms.ssContactShadowDistance = shadowMappingUniforms.ssContactShadowDistance; - uniforms.directionalShadows = shadowMappingUniforms.directionalShadows; - uniforms.cascades = shadowMappingUniforms.cascades; + uniforms.directionalShadows = int32_t(shadowMappingUniforms.directionalShadows); + uniforms.cascades = int32_t(shadowMappingUniforms.cascades); } -void PerViewUniforms::prepareShadowVSM(Handle texture, +void ColorPassDescriptorSet::prepareShadowVSM(Handle texture, ShadowMappingUniforms const& shadowMappingUniforms, VsmShadowOptions const& options) noexcept { constexpr float low = 5.54f; // ~ std::log(std::numeric_limits::max()) * 0.5f; @@ -384,85 +469,93 @@ void PerViewUniforms::prepareShadowVSM(Handle texture, if (options.anisotropy > 0 || options.mipmapping) { filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR; } - mSamplers.setSampler(PerViewSib::SHADOW_MAP, { + setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, { .filterMag = SamplerMagFilter::LINEAR, .filterMin = filterMin, .anisotropyLog2 = options.anisotropy, - }}); + }); auto& s = mUniforms.edit(); s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_EVSM; s.vsmExponent = options.highPrecision ? high : low; s.vsmDepthScale = options.minVarianceScale * 0.01f * s.vsmExponent; s.vsmLightBleedReduction = options.lightBleedReduction; - PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms); + ColorPassDescriptorSet::prepareShadowSampling(s, shadowMappingUniforms); } -void PerViewUniforms::prepareShadowPCF(Handle texture, +void ColorPassDescriptorSet::prepareShadowPCF(Handle texture, ShadowMappingUniforms const& shadowMappingUniforms) noexcept { - mSamplers.setSampler(PerViewSib::SHADOW_MAP, { + setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, { .filterMag = SamplerMagFilter::LINEAR, .filterMin = SamplerMinFilter::LINEAR, .compareMode = SamplerCompareMode::COMPARE_TO_TEXTURE, .compareFunc = SamplerCompareFunc::GE - }}); + }); auto& s = mUniforms.edit(); s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF; - PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms); + ColorPassDescriptorSet::prepareShadowSampling(s, shadowMappingUniforms); } -void PerViewUniforms::prepareShadowDPCF(Handle texture, +void ColorPassDescriptorSet::prepareShadowDPCF(Handle texture, ShadowMappingUniforms const& shadowMappingUniforms, SoftShadowOptions const& options) noexcept { - mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {}}); + setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, {}); auto& s = mUniforms.edit(); s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_DPCF; s.shadowPenumbraRatioScale = options.penumbraRatioScale; - PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms); + ColorPassDescriptorSet::prepareShadowSampling(s, shadowMappingUniforms); } -void PerViewUniforms::prepareShadowPCSS(Handle texture, +void ColorPassDescriptorSet::prepareShadowPCSS(Handle texture, ShadowMappingUniforms const& shadowMappingUniforms, SoftShadowOptions const& options) noexcept { - mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, {}}); + setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, {}); auto& s = mUniforms.edit(); s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCSS; s.shadowPenumbraRatioScale = options.penumbraRatioScale; - PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms); + ColorPassDescriptorSet::prepareShadowSampling(s, shadowMappingUniforms); } -void PerViewUniforms::prepareShadowPCFDebug(Handle texture, +void ColorPassDescriptorSet::prepareShadowPCFDebug(Handle texture, ShadowMappingUniforms const& shadowMappingUniforms) noexcept { - mSamplers.setSampler(PerViewSib::SHADOW_MAP, { texture, { + setSampler(+PerViewBindingPoints::SHADOW_MAP, texture, { .filterMag = SamplerMagFilter::NEAREST, .filterMin = SamplerMinFilter::NEAREST - }}); + }); auto& s = mUniforms.edit(); s.shadowSamplingType = SHADOW_SAMPLING_RUNTIME_PCF; - PerViewUniforms::prepareShadowSampling(s, shadowMappingUniforms); + ColorPassDescriptorSet::prepareShadowSampling(s, shadowMappingUniforms); } -void PerViewUniforms::commit(backend::DriverApi& driver) noexcept { +void ColorPassDescriptorSet::commit(backend::DriverApi& driver) noexcept { if (mUniforms.isDirty()) { - driver.updateBufferObject(mUniformBufferHandle, mUniforms.toBufferDescriptor(driver), 0); + driver.updateBufferObject(mUniforms.getUboHandle(), + mUniforms.toBufferDescriptor(driver), 0); } - if (mSamplers.isDirty()) { - driver.updateSamplerGroup(mSamplerGroupHandle, mSamplers.toBufferDescriptor(driver)); + for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) { + mDescriptorSet[i].commit(mDescriptorSetLayout[i], driver); } } -void PerViewUniforms::bind(backend::DriverApi& driver) noexcept { - driver.bindUniformBuffer(+UniformBindingPoints::PER_VIEW, mUniformBufferHandle); - driver.bindSamplers(+SamplerBindingPoints::PER_VIEW, mSamplerGroupHandle); +void ColorPassDescriptorSet::setSampler(backend::descriptor_binding_t binding, + TextureHandle th, SamplerParams params) noexcept { + for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) { + auto samplers = mDescriptorSetLayout[i].getSamplerDescriptors(); + if (samplers[binding]) { + mDescriptorSet[i].setSampler(binding, th, params); + } + } } -void PerViewUniforms::unbindSamplers() noexcept { - auto& samplerGroup = mSamplers; - samplerGroup.clearSampler(PerViewSib::SSAO); - samplerGroup.clearSampler(PerViewSib::SSR); - samplerGroup.clearSampler(PerViewSib::STRUCTURE); - samplerGroup.clearSampler(PerViewSib::SHADOW_MAP); +void ColorPassDescriptorSet::setBuffer(backend::descriptor_binding_t binding, + BufferObjectHandle boh, uint32_t offset, uint32_t size) noexcept { + for (size_t i = 0; i < DESCRIPTOR_LAYOUT_COUNT; i++) { + auto ubos = mDescriptorSetLayout[i].getUniformBufferDescriptors(); + if (ubos[binding]) { + mDescriptorSet[i].setBuffer(binding, boh, offset, size); + } + } } } // namespace filament diff --git a/filament/src/PerViewUniforms.h b/filament/src/ds/ColorPassDescriptorSet.h similarity index 78% rename from filament/src/PerViewUniforms.h rename to filament/src/ds/ColorPassDescriptorSet.h index c8df0661903..3c3bc04df21 100644 --- a/filament/src/PerViewUniforms.h +++ b/filament/src/ds/ColorPassDescriptorSet.h @@ -19,19 +19,31 @@ #include -#include -#include +#include "DescriptorSet.h" #include "TypedUniformBuffer.h" +#include + +#include #include #include -#include +#include +#include +#include +#include + +#include + +#include +#include namespace filament { +class DescriptorSetLayout; + struct AmbientOcclusionOptions; struct DynamicResolutionOptions; struct FogOptions; @@ -53,7 +65,8 @@ class LightManager; * holds onto handles for the PER_VIEW UBO and SamplerGroup. This class maintains a shadow copy * of the UBO/sampler data, so it is possible to partially update it between commits. */ -class PerViewUniforms { + +class ColorPassDescriptorSet { using LightManagerInstance = utils::EntityInstance; using TextureHandle = backend::Handle; @@ -64,7 +77,16 @@ class PerViewUniforms { static constexpr uint32_t const SHADOW_SAMPLING_RUNTIME_PCSS = 3u; public: - explicit PerViewUniforms(FEngine& engine) noexcept; + + static uint8_t getIndex(bool lit, bool ssr, bool fog) noexcept; + + ColorPassDescriptorSet(FEngine& engine, + TypedUniformBuffer& uniforms) noexcept; + + void init( + backend::BufferObjectHandle lights, + backend::BufferObjectHandle recordBuffer, + backend::BufferObjectHandle froxelBuffer) noexcept; void terminate(backend::DriverApi& driver); @@ -104,7 +126,7 @@ class PerViewUniforms { math::mat4f const& uvFromViewMatrix, ScreenSpaceReflectionsOptions const& ssrOptions) noexcept; - void prepareShadowMapping(bool highPrecision) noexcept; + void prepareShadowMapping(backend::BufferObjectHandle shadowUniforms, bool highPrecision) noexcept; void prepareDirectionalLight(FEngine& engine, float exposure, math::float3 const& sceneSpaceDirection, LightManagerInstance instance) noexcept; @@ -136,15 +158,22 @@ class PerViewUniforms { void commit(backend::DriverApi& driver) noexcept; // bind this UBO - void bind(backend::DriverApi& driver) noexcept; - - void unbindSamplers() noexcept; + void bind(backend::DriverApi& driver, uint8_t index) const noexcept { + mDescriptorSet[index].bind(driver, DescriptorSetBindingPoints::PER_VIEW); + } private: - TypedUniformBuffer mUniforms; - backend::SamplerGroup mSamplers; - backend::Handle mUniformBufferHandle; - backend::Handle mSamplerGroupHandle; + static constexpr size_t DESCRIPTOR_LAYOUT_COUNT = 8; + + void setSampler(backend::descriptor_binding_t binding, + backend::TextureHandle th, backend::SamplerParams params) noexcept; + + void setBuffer(backend::descriptor_binding_t binding, + backend::BufferObjectHandle boh, uint32_t offset, uint32_t size) noexcept; + + TypedUniformBuffer& mUniforms; + std::array mDescriptorSetLayout; + std::array mDescriptorSet; static void prepareShadowSampling(PerViewUib& uniforms, ShadowMappingUniforms const& shadowMappingUniforms) noexcept; }; diff --git a/filament/src/ds/DescriptorSet.cpp b/filament/src/ds/DescriptorSet.cpp new file mode 100644 index 00000000000..722224c578a --- /dev/null +++ b/filament/src/ds/DescriptorSet.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DescriptorSet.h" + +#include "DescriptorSetLayout.h" + +#include "details/Engine.h" + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +namespace filament { + +DescriptorSet::DescriptorSet() noexcept = default; + +DescriptorSet::~DescriptorSet() noexcept { + // make sure we're not leaking the descriptor set handle + assert_invariant(!mDescriptorSetHandle); +} + +DescriptorSet::DescriptorSet(DescriptorSetLayout const& descriptorSetLayout) noexcept + : mDescriptors(descriptorSetLayout.getMaxDescriptorBinding() + 1), + mDirty(std::numeric_limits::max()) { +} + +DescriptorSet::DescriptorSet(DescriptorSet&& rhs) noexcept = default; + +DescriptorSet& DescriptorSet::operator=(DescriptorSet&& rhs) noexcept { + if (this != &rhs) { + // make sure we're not leaking the descriptor set handle + assert_invariant(!mDescriptorSetHandle); + mDescriptors = std::move(rhs.mDescriptors); + mDescriptorSetHandle = std::move(rhs.mDescriptorSetHandle); + mDirty = rhs.mDirty; + mValid = rhs.mValid; + } + return *this; +} + +void DescriptorSet::terminate(FEngine::DriverApi& driver) noexcept { + if (mDescriptorSetHandle) { + driver.destroyDescriptorSet(mDescriptorSetHandle); + mDescriptorSetHandle.clear(); + } +} + +void DescriptorSet::commitSlow(DescriptorSetLayout const& layout, + FEngine::DriverApi& driver) noexcept { + mDirty.clear(); + // if we have a dirty descriptor set, + // we need to allocate a new one and reset all the descriptors + if (UTILS_LIKELY(mDescriptorSetHandle)) { + // note: if the descriptor-set is bound, doing this will essentially make it dangling. + // This can result in a use-after-free in the driver if the new one isn't bound at some + // point later. + driver.destroyDescriptorSet(mDescriptorSetHandle); + } + mDescriptorSetHandle = driver.createDescriptorSet(layout.getHandle()); + mValid.forEachSetBit([&layout, &driver, + dsh = mDescriptorSetHandle, descriptors = mDescriptors.data()] + (backend::descriptor_binding_t const binding) { + if (layout.isSampler(binding)) { + driver.updateDescriptorSetTexture(dsh, binding, + descriptors[binding].texture.th, + descriptors[binding].texture.params); + } else { + driver.updateDescriptorSetBuffer(dsh, binding, + descriptors[binding].buffer.boh, + descriptors[binding].buffer.offset, + descriptors[binding].buffer.size); + } + }); +} + +void DescriptorSet::bind(FEngine::DriverApi& driver, DescriptorSetBindingPoints set) const noexcept { + bind(driver, set, {}); +} + +void DescriptorSet::bind(FEngine::DriverApi& driver, DescriptorSetBindingPoints set, + backend::DescriptorSetOffsetArray dynamicOffsets) const noexcept { + // TODO: on debug check that dynamicOffsets is large enough + assert_invariant(mDirty.none()); + assert_invariant(mDescriptorSetHandle); + driver.bindDescriptorSet(mDescriptorSetHandle, +set, std::move(dynamicOffsets)); +} + +void DescriptorSet::setBuffer( + backend::descriptor_binding_t binding, + backend::Handle boh, uint32_t offset, uint32_t size) noexcept { + // TODO: validate it's the right kind of descriptor + if (mDescriptors[binding].buffer.boh != boh || mDescriptors[binding].buffer.size != size) { + // we don't set the dirty bit if only offset changes + mDirty.set(binding); + } + mDescriptors[binding].buffer = { boh, offset, size }; + mValid.set(binding, (bool)boh); +} + +void DescriptorSet::setSampler( + backend::descriptor_binding_t binding, + backend::Handle th, backend::SamplerParams params) noexcept { + // TODO: validate it's the right kind of descriptor + if (mDescriptors[binding].texture.th != th || mDescriptors[binding].texture.params != params) { + mDirty.set(binding); + } + mDescriptors[binding].texture = { th, params }; + mValid.set(binding, (bool)th); +} + +} // namespace filament diff --git a/filament/src/ds/DescriptorSet.h b/filament/src/ds/DescriptorSet.h new file mode 100644 index 00000000000..6cc861efd96 --- /dev/null +++ b/filament/src/ds/DescriptorSet.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DETAILS_DESCRIPTORSET_H +#define TNT_FILAMENT_DETAILS_DESCRIPTORSET_H + +#include "DescriptorSetLayout.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace filament { + +class DescriptorSet { +public: + DescriptorSet() noexcept; + explicit DescriptorSet(DescriptorSetLayout const& descriptorSetLayout) noexcept; + DescriptorSet(DescriptorSet const&) = delete; + DescriptorSet(DescriptorSet&& rhs) noexcept; + DescriptorSet& operator=(DescriptorSet const&) = delete; + DescriptorSet& operator=(DescriptorSet&& rhs) noexcept; + ~DescriptorSet() noexcept; + + void terminate(backend::DriverApi& driver) noexcept; + + // update the descriptors if needed + void commit(DescriptorSetLayout const& layout, + backend::DriverApi& driver) noexcept { + if (UTILS_UNLIKELY(mDirty.any())) { + commitSlow(layout, driver); + } + } + + // bind the descriptor set + void bind(backend::DriverApi& driver, DescriptorSetBindingPoints set) const noexcept; + + void bind(backend::DriverApi& driver, DescriptorSetBindingPoints set, + backend::DescriptorSetOffsetArray dynamicOffsets) const noexcept; + + // sets a ubo/ssbo descriptor + void setBuffer(backend::descriptor_binding_t binding, + backend::Handle boh, + uint32_t offset, uint32_t size) noexcept; + + // sets a sampler descriptor + void setSampler(backend::descriptor_binding_t binding, + backend::Handle th, + backend::SamplerParams params) noexcept; + + backend::DescriptorSetHandle getHandle() const noexcept { + return mDescriptorSetHandle; + } + + utils::bitset64 getValidDescriptors() const noexcept { + return mValid; + } + +private: + void commitSlow(DescriptorSetLayout const& layout, backend::DriverApi& driver) noexcept; + + struct Desc { + Desc() noexcept { } + union { + struct { + backend::Handle boh; + uint32_t offset; + uint32_t size; + } buffer{}; + struct { + backend::Handle th; + backend::SamplerParams params; + uint32_t padding; + } texture; + }; + }; + + utils::FixedCapacityVector mDescriptors; // 16 + mutable utils::bitset64 mDirty; // 8 + mutable utils::bitset64 mValid; // 8 + backend::DescriptorSetHandle mDescriptorSetHandle; // 4 +}; + +} // namespace filament + +#endif //TNT_FILAMENT_DETAILS_DESCRIPTORSET_H diff --git a/filament/src/ds/DescriptorSetLayout.cpp b/filament/src/ds/DescriptorSetLayout.cpp new file mode 100644 index 00000000000..646a8f5d6cb --- /dev/null +++ b/filament/src/ds/DescriptorSetLayout.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DescriptorSetLayout.h" + +#include "details/Engine.h" + +#include + +#include +#include + +namespace filament { + +DescriptorSetLayout::DescriptorSetLayout() noexcept = default; + +DescriptorSetLayout::DescriptorSetLayout(backend::DriverApi& driver, + backend::DescriptorSetLayout descriptorSetLayout) noexcept { + for (auto&& desc : descriptorSetLayout.bindings) { + mMaxDescriptorBinding = std::max(mMaxDescriptorBinding, desc.binding); + mSamplers.set(desc.binding, desc.type == backend::DescriptorType::SAMPLER); + mUniformBuffers.set(desc.binding, desc.type == backend::DescriptorType::UNIFORM_BUFFER); + } + + mDescriptorSetLayoutHandle = driver.createDescriptorSetLayout( + std::move(descriptorSetLayout)); +} + +void DescriptorSetLayout::terminate(backend::DriverApi& driver) noexcept { + if (mDescriptorSetLayoutHandle) { + driver.destroyDescriptorSetLayout(mDescriptorSetLayoutHandle); + } +} + +DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& rhs) noexcept = default; + +DescriptorSetLayout& DescriptorSetLayout::operator=(DescriptorSetLayout&& rhs) noexcept = default; + +} // namespace filament diff --git a/filament/src/ds/DescriptorSetLayout.h b/filament/src/ds/DescriptorSetLayout.h new file mode 100644 index 00000000000..44c2b00ba19 --- /dev/null +++ b/filament/src/ds/DescriptorSetLayout.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DESCRIPTORSETLAYOUT_H +#define TNT_FILAMENT_DESCRIPTORSETLAYOUT_H + +#include + +#include +#include + +#include + +#include +#include + +namespace filament { + +class DescriptorSetLayout { +public: + DescriptorSetLayout() noexcept; + DescriptorSetLayout(backend::DriverApi& driver, + backend::DescriptorSetLayout descriptorSetLayout) noexcept; + + DescriptorSetLayout(DescriptorSetLayout const&) = delete; + DescriptorSetLayout(DescriptorSetLayout&& rhs) noexcept; + DescriptorSetLayout& operator=(DescriptorSetLayout const&) = delete; + DescriptorSetLayout& operator=(DescriptorSetLayout&& rhs) noexcept; + + void terminate(backend::DriverApi& driver) noexcept; + + backend::DescriptorSetLayoutHandle getHandle() const noexcept { + return mDescriptorSetLayoutHandle; + } + + size_t getMaxDescriptorBinding() const noexcept { + return mMaxDescriptorBinding; + } + + bool isSampler(backend::descriptor_binding_t binding) const noexcept { + return mSamplers[binding]; + } + + utils::bitset64 getSamplerDescriptors() const noexcept { + return mSamplers; + } + + utils::bitset64 getUniformBufferDescriptors() const noexcept { + return mUniformBuffers; + } + +private: + backend::DescriptorSetLayoutHandle mDescriptorSetLayoutHandle; + utils::bitset64 mSamplers; + utils::bitset64 mUniformBuffers; + uint8_t mMaxDescriptorBinding = 0; +}; + + +} // namespace filament + +#endif //TNT_FILAMENT_DESCRIPTORSETLAYOUT_H diff --git a/filament/src/ds/PostProcessDescriptorSet.cpp b/filament/src/ds/PostProcessDescriptorSet.cpp new file mode 100644 index 00000000000..3dc0baf312e --- /dev/null +++ b/filament/src/ds/PostProcessDescriptorSet.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PostProcessDescriptorSet.h" + +#include "TypedUniformBuffer.h" + +#include "details/Engine.h" + +#include +#include +#include + +#include + +namespace filament { + +using namespace backend; +using namespace math; + +PostProcessDescriptorSet::PostProcessDescriptorSet() noexcept = default; + +void PostProcessDescriptorSet::init(FEngine& engine) noexcept { + + // create the descriptor-set layout + mDescriptorSetLayout = filament::DescriptorSetLayout{ + engine.getDriverApi(), descriptor_sets::getPostProcessLayout() }; + + // create the descriptor-set from the layout + mDescriptorSet = DescriptorSet{ mDescriptorSetLayout }; +} + +void PostProcessDescriptorSet::terminate(DriverApi& driver) { + mDescriptorSet.terminate(driver); + mDescriptorSetLayout.terminate(driver); +} + +void PostProcessDescriptorSet::setFrameUniforms(DriverApi& driver, + TypedUniformBuffer& uniforms) noexcept { + // initialize the descriptor-set + mDescriptorSet.setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS, + uniforms.getUboHandle(), 0, uniforms.getSize()); + + mDescriptorSet.commit(mDescriptorSetLayout, driver); +} + +void PostProcessDescriptorSet::bind(backend::DriverApi& driver) noexcept { + mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_VIEW); +} + +} // namespace filament + diff --git a/filament/src/ds/PostProcessDescriptorSet.h b/filament/src/ds/PostProcessDescriptorSet.h new file mode 100644 index 00000000000..edc1b584051 --- /dev/null +++ b/filament/src/ds/PostProcessDescriptorSet.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_POSTPROCESSINGDESCRIPTORSET_H +#define TNT_FILAMENT_POSTPROCESSINGDESCRIPTORSET_H + +#include "DescriptorSet.h" + +#include "DescriptorSetLayout.h" + +#include "TypedUniformBuffer.h" + +#include + +#include + +namespace filament { + +class FEngine; + +class PostProcessDescriptorSet { +public: + explicit PostProcessDescriptorSet() noexcept; + + void init(FEngine& engine) noexcept; + + void terminate(backend::DriverApi& driver); + + void setFrameUniforms(backend::DriverApi& driver, + TypedUniformBuffer& uniforms) noexcept; + + void bind(backend::DriverApi& driver) noexcept; + + DescriptorSetLayout const& getLayout() const noexcept { + return mDescriptorSetLayout; + } + +private: + DescriptorSetLayout mDescriptorSetLayout; + DescriptorSet mDescriptorSet; +}; + +} // namespace filament + +#endif //TNT_FILAMENT_POSTPROCESSINGDESCRIPTORSET_H diff --git a/filament/src/PerShadowMapUniforms.cpp b/filament/src/ds/ShadowMapDescriptorSet.cpp similarity index 72% rename from filament/src/PerShadowMapUniforms.cpp rename to filament/src/ds/ShadowMapDescriptorSet.cpp index 458b15db836..ae6aacaaede 100644 --- a/filament/src/PerShadowMapUniforms.cpp +++ b/filament/src/ds/ShadowMapDescriptorSet.cpp @@ -14,12 +14,13 @@ * limitations under the License. */ -#include "PerShadowMapUniforms.h" +#include "ShadowMapDescriptorSet.h" #include "details/Camera.h" #include "details/Engine.h" #include +#include #include #include @@ -35,22 +36,31 @@ namespace filament { using namespace backend; using namespace math; -PerShadowMapUniforms::PerShadowMapUniforms(FEngine& engine) noexcept { +ShadowMapDescriptorSet::ShadowMapDescriptorSet(FEngine& engine) noexcept { DriverApi& driver = engine.getDriverApi(); + mUniformBufferHandle = driver.createBufferObject(sizeof(PerViewUib), BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC); + + // create the descriptor-set from the layout + mDescriptorSet = DescriptorSet{ engine.getPerViewDescriptorSetLayoutDepthVariant() }; + + // initialize the descriptor-set + mDescriptorSet.setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS, + mUniformBufferHandle, 0, sizeof(PerViewUib)); } -void PerShadowMapUniforms::terminate(DriverApi& driver) { +void ShadowMapDescriptorSet::terminate(DriverApi& driver) { + mDescriptorSet.terminate(driver); driver.destroyBufferObject(mUniformBufferHandle); } -PerViewUib& PerShadowMapUniforms::edit(Transaction const& transaction) noexcept { +PerViewUib& ShadowMapDescriptorSet::edit(Transaction const& transaction) noexcept { assert_invariant(transaction.uniforms); return *transaction.uniforms; } -void PerShadowMapUniforms::prepareCamera(Transaction const& transaction, +void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction, FEngine& engine, const CameraInfo& camera) noexcept { mat4f const& viewFromWorld = camera.view; mat4f const& worldFromView = camera.model; @@ -78,12 +88,12 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction, s.clipControl = engine.getDriverApi().getClipSpaceParams(); } -void PerShadowMapUniforms::prepareLodBias(Transaction const& transaction, float bias) noexcept { +void ShadowMapDescriptorSet::prepareLodBias(Transaction const& transaction, float bias) noexcept { auto& s = edit(transaction); s.lodBias = bias; } -void PerShadowMapUniforms::prepareViewport(Transaction const& transaction, +void ShadowMapDescriptorSet::prepareViewport(Transaction const& transaction, backend::Viewport const& viewport) noexcept { float2 const dimensions{ viewport.width, viewport.height }; auto& s = edit(transaction); @@ -92,7 +102,7 @@ void PerShadowMapUniforms::prepareViewport(Transaction const& transaction, s.logicalViewportOffset = 0.0f; } -void PerShadowMapUniforms::prepareTime(Transaction const& transaction, +void ShadowMapDescriptorSet::prepareTime(Transaction const& transaction, FEngine& engine, math::float4 const& userTime) noexcept { auto& s = edit(transaction); const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1'000'000'000; @@ -101,7 +111,7 @@ void PerShadowMapUniforms::prepareTime(Transaction const& transaction, s.userTime = userTime; } -void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction, +void ShadowMapDescriptorSet::prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept { auto& s = edit(transaction); constexpr float low = 5.54f; // ~ std::log(std::numeric_limits::max()) * 0.5f; @@ -109,7 +119,7 @@ void PerShadowMapUniforms::prepareShadowMapping(Transaction const& transaction, s.vsmExponent = highPrecision ? high : low; } -PerShadowMapUniforms::Transaction PerShadowMapUniforms::open(backend::DriverApi& driver) noexcept { +ShadowMapDescriptorSet::Transaction ShadowMapDescriptorSet::open(backend::DriverApi& driver) noexcept { Transaction transaction; // TODO: use out-of-line buffer if too large transaction.uniforms = (PerViewUib *)driver.allocate(sizeof(PerViewUib), 16); @@ -117,15 +127,16 @@ PerShadowMapUniforms::Transaction PerShadowMapUniforms::open(backend::DriverApi& return transaction; } -void PerShadowMapUniforms::commit(Transaction& transaction, - backend::DriverApi& driver) noexcept { +void ShadowMapDescriptorSet::commit(Transaction& transaction, + FEngine& engine, backend::DriverApi& driver) noexcept { driver.updateBufferObject(mUniformBufferHandle, { transaction.uniforms, sizeof(PerViewUib) }, 0); + mDescriptorSet.commit(engine.getPerViewDescriptorSetLayoutDepthVariant(), driver); transaction.uniforms = nullptr; } -void PerShadowMapUniforms::bind(backend::DriverApi& driver) noexcept { - driver.bindUniformBuffer(+UniformBindingPoints::PER_VIEW, mUniformBufferHandle); +void ShadowMapDescriptorSet::bind(backend::DriverApi& driver) noexcept { + mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_VIEW); } } // namespace filament diff --git a/filament/src/PerShadowMapUniforms.h b/filament/src/ds/ShadowMapDescriptorSet.h similarity index 81% rename from filament/src/PerShadowMapUniforms.h rename to filament/src/ds/ShadowMapDescriptorSet.h index 05fcdd82e5f..c1f55c5c89d 100644 --- a/filament/src/PerShadowMapUniforms.h +++ b/filament/src/ds/ShadowMapDescriptorSet.h @@ -14,10 +14,14 @@ * limitations under the License. */ -#ifndef TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H -#define TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H +#ifndef TNT_FILAMENT_SHADOWMAPDESCRIPTORSET_H +#define TNT_FILAMENT_SHADOWMAPDESCRIPTORSET_H -#include +#include "DescriptorSet.h" + +#include "DescriptorSetLayout.h" + +#include "private/filament/UibStructs.h" #include #include @@ -38,16 +42,16 @@ class LightManager; * writes the data directly into the CommandStream, for this reason partial update of the data * is not possible. */ -class PerShadowMapUniforms { +class ShadowMapDescriptorSet { public: class Transaction { - friend PerShadowMapUniforms; + friend ShadowMapDescriptorSet; PerViewUib* uniforms = nullptr; Transaction() = default; // disallow creation by the caller }; - explicit PerShadowMapUniforms(FEngine& engine) noexcept; + explicit ShadowMapDescriptorSet(FEngine& engine) noexcept; void terminate(backend::DriverApi& driver); @@ -69,7 +73,7 @@ class PerShadowMapUniforms { static Transaction open(backend::DriverApi& driver) noexcept; // update local data into GPU UBO - void commit(Transaction& transaction, backend::DriverApi& driver) noexcept; + void commit(Transaction& transaction, FEngine& engine, backend::DriverApi& driver) noexcept; // bind this UBO void bind(backend::DriverApi& driver) noexcept; @@ -77,8 +81,9 @@ class PerShadowMapUniforms { private: static PerViewUib& edit(Transaction const& transaction) noexcept; backend::Handle mUniformBufferHandle; + DescriptorSet mDescriptorSet; }; } // namespace filament -#endif //TNT_FILAMENT_PERSHADOWMAPUNIFORMS_H +#endif //TNT_FILAMENT_SHADOWMAPDESCRIPTORSET_H diff --git a/filament/src/ds/SsrPassDescriptorSet.cpp b/filament/src/ds/SsrPassDescriptorSet.cpp new file mode 100644 index 00000000000..693b7815af2 --- /dev/null +++ b/filament/src/ds/SsrPassDescriptorSet.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SsrPassDescriptorSet.h" + +#include "TypedUniformBuffer.h" + +#include "details/Engine.h" + +#include +#include + +#include +#include + +#include + +#include + +#include + +namespace filament { + +using namespace backend; +using namespace math; + +SsrPassDescriptorSet::SsrPassDescriptorSet() noexcept = default; + +void SsrPassDescriptorSet::init(FEngine& engine) noexcept { + // create the descriptor-set from the layout + mDescriptorSet = DescriptorSet{ engine.getPerViewDescriptorSetLayoutSsrVariant() }; +} + +void SsrPassDescriptorSet::terminate(DriverApi& driver) { + mDescriptorSet.terminate(driver); +} + +void SsrPassDescriptorSet::setFrameUniforms(TypedUniformBuffer& uniforms) noexcept { + // initialize the descriptor-set + mDescriptorSet.setBuffer(+PerViewBindingPoints::FRAME_UNIFORMS, + uniforms.getUboHandle(), 0, uniforms.getSize()); + + mUniforms = std::addressof(uniforms); +} + +void SsrPassDescriptorSet::prepareHistorySSR(Handle ssr, + math::mat4f const& historyProjection, + math::mat4f const& uvFromViewMatrix, + ScreenSpaceReflectionsOptions const& ssrOptions) noexcept { + + mDescriptorSet.setSampler(+PerViewBindingPoints::SSR, ssr, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR + }); + + assert_invariant(mUniforms); + auto& s = mUniforms->edit(); + s.ssrReprojection = historyProjection; + s.ssrUvFromViewMatrix = uvFromViewMatrix; + s.ssrThickness = ssrOptions.thickness; + s.ssrBias = ssrOptions.bias; + s.ssrDistance = ssrOptions.enabled ? ssrOptions.maxDistance : 0.0f; + s.ssrStride = ssrOptions.stride; +} + +void SsrPassDescriptorSet::prepareStructure(Handle structure) noexcept { + // sampler must be NEAREST + mDescriptorSet.setSampler(+PerViewBindingPoints::STRUCTURE, structure, {}); +} + +void SsrPassDescriptorSet::commit(FEngine& engine) noexcept { + assert_invariant(mUniforms); + DriverApi& driver = engine.getDriverApi(); + if (mUniforms->isDirty()) { + driver.updateBufferObject(mUniforms->getUboHandle(), + mUniforms->toBufferDescriptor(driver), 0); + } + mDescriptorSet.commit(engine.getPerViewDescriptorSetLayoutSsrVariant(), driver); +} + +void SsrPassDescriptorSet::bind(backend::DriverApi& driver) noexcept { + mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_VIEW); +} + +} // namespace filament + diff --git a/filament/src/ds/SsrPassDescriptorSet.h b/filament/src/ds/SsrPassDescriptorSet.h new file mode 100644 index 00000000000..161c4ff6511 --- /dev/null +++ b/filament/src/ds/SsrPassDescriptorSet.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_SSRPASSDESCRIPTORSET_H +#define TNT_FILAMENT_SSRPASSDESCRIPTORSET_H + +#include "DescriptorSet.h" + +#include "DescriptorSetLayout.h" + +#include "TypedUniformBuffer.h" + +#include + +#include +#include + +#include + +namespace filament { + +class FEngine; + +struct ScreenSpaceReflectionsOptions; + +class SsrPassDescriptorSet { + + using TextureHandle = backend::Handle; + +public: + SsrPassDescriptorSet() noexcept; + + void init(FEngine& engine) noexcept; + + void terminate(backend::DriverApi& driver); + + void setFrameUniforms(TypedUniformBuffer& uniforms) noexcept; + + void prepareStructure(TextureHandle structure) noexcept; + + void prepareHistorySSR(TextureHandle ssr, + math::mat4f const& historyProjection, + math::mat4f const& uvFromViewMatrix, + ScreenSpaceReflectionsOptions const& ssrOptions) noexcept; + + + // update local data into GPU UBO + void commit(FEngine& engine) noexcept; + + // bind this descriptor set + void bind(backend::DriverApi& driver) noexcept; + +private: + TypedUniformBuffer* mUniforms = nullptr; + DescriptorSet mDescriptorSet; +}; + +} // namespace filament + +#endif //TNT_FILAMENT_SSRPASSDESCRIPTORSET_H diff --git a/filament/src/TypedUniformBuffer.h b/filament/src/ds/TypedBuffer.h similarity index 87% rename from filament/src/TypedUniformBuffer.h rename to filament/src/ds/TypedBuffer.h index 264784dbf68..ea55b7c43d2 100644 --- a/filament/src/TypedUniformBuffer.h +++ b/filament/src/ds/TypedBuffer.h @@ -14,21 +14,20 @@ * limitations under the License. */ -#ifndef TNT_FILAMENT_TYPEDUNIFORMBUFFER_H -#define TNT_FILAMENT_TYPEDUNIFORMBUFFER_H +#ifndef TNT_FILAMENT_TYPEDBUFFER_H +#define TNT_FILAMENT_TYPEDBUFFER_H -#include "private/backend/DriverApi.h" - -#include +#include #include #include +#include namespace filament { template -class TypedUniformBuffer { // NOLINT(cppcoreguidelines-pro-type-member-init) +class TypedBuffer { // NOLINT(cppcoreguidelines-pro-type-member-init) public: T& itemAt(size_t i) noexcept { @@ -73,4 +72,4 @@ class TypedUniformBuffer { // NOLINT(cppcoreguidelines-pro-type-member-init) } // namespace filament -#endif // TNT_FILAMENT_TYPEDUNIFORMBUFFER_H +#endif // TNT_FILAMENT_TYPEDBUFFER_H diff --git a/filament/src/ds/TypedUniformBuffer.h b/filament/src/ds/TypedUniformBuffer.h new file mode 100644 index 00000000000..44d26995667 --- /dev/null +++ b/filament/src/ds/TypedUniformBuffer.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_TYPEDUNIFORMBUFFER_H +#define TNT_FILAMENT_TYPEDUNIFORMBUFFER_H + +#include "TypedBuffer.h" + +#include +#include +#include + +#include + +namespace filament { + +template +class TypedUniformBuffer { +public: + + explicit TypedUniformBuffer(backend::DriverApi& driver) noexcept { + mUboHandle = driver.createBufferObject( + mTypedBuffer.getSize(), + backend::BufferObjectBinding::UNIFORM, + backend::BufferUsage::DYNAMIC); + } + + void terminate(backend::DriverApi& driver) noexcept { + driver.destroyBufferObject(mUboHandle); + } + + TypedBuffer& getTypedBuffer() noexcept { + return mTypedBuffer; + } + + backend::BufferObjectHandle getUboHandle() const noexcept { + return mUboHandle; + } + + T& itemAt(size_t i) noexcept { + return mTypedBuffer.itemAt(i); + } + + T& edit() noexcept { + return mTypedBuffer.itemAt(0); + } + + // size of the uniform buffer in bytes + size_t getSize() const noexcept { return mTypedBuffer.getSize(); } + + // return if any uniform has been changed + bool isDirty() const noexcept { return mTypedBuffer.isDirty(); } + + // mark the whole buffer as "clean" (no modified uniforms) + void clean() const noexcept { mTypedBuffer.clean(); } + + // helper functions + backend::BufferDescriptor toBufferDescriptor(backend::DriverApi& driver) const noexcept { + return mTypedBuffer.toBufferDescriptor(driver); + } + + // copy the UBO data and cleans the dirty bits + backend::BufferDescriptor toBufferDescriptor( + backend::DriverApi& driver, size_t offset, size_t size) const noexcept { + return mTypedBuffer.toBufferDescriptor(driver, offset, size); + } + +private: + TypedBuffer mTypedBuffer; + backend::BufferObjectHandle mUboHandle; +}; + +} // namespace filament + + +#endif //TNT_FILAMENT_TYPEDUNIFORMBUFFER_H diff --git a/libs/filabridge/CMakeLists.txt b/libs/filabridge/CMakeLists.txt index ffcd1ce2367..6840ce536e7 100644 --- a/libs/filabridge/CMakeLists.txt +++ b/libs/filabridge/CMakeLists.txt @@ -10,8 +10,9 @@ set(PUBLIC_HDR_DIR include) file(GLOB_RECURSE PUBLIC_HDRS ${PUBLIC_HDR_DIR}/**/*.h) set(SRCS - src/SamplerInterfaceBlock.cpp src/BufferInterfaceBlock.cpp + src/DescriptorSets.cpp + src/SamplerInterfaceBlock.cpp src/Variant.cpp ) diff --git a/libs/filabridge/include/filament/MaterialChunkType.h b/libs/filabridge/include/filament/MaterialChunkType.h index 4a4561c14a9..5ad99cceb69 100644 --- a/libs/filabridge/include/filament/MaterialChunkType.h +++ b/libs/filabridge/include/filament/MaterialChunkType.h @@ -47,10 +47,10 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialMetal = charTo64bitNum("MAT_METL"), MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"), MaterialShaderModels = charTo64bitNum("MAT_SMDL"), - MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), - MaterialUniformBindings = charTo64bitNum("MAT_UNIF"), MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"), MaterialAttributeInfo = charTo64bitNum("MAT_ATTR"), + MaterialDescriptorBindingsInfo = charTo64bitNum("MAT_DBDI"), + MaterialDescriptorSetLayoutInfo = charTo64bitNum("MAT_DSLI"), MaterialProperties = charTo64bitNum("MAT_PROP"), MaterialConstants = charTo64bitNum("MAT_CONS"), MaterialPushConstants = charTo64bitNum("MAT_PCON"), diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index c15c6c457c8..9178f6d5e3c 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -255,4 +255,7 @@ enum class UserVariantFilterBit : UserVariantFilterMask { template<> struct utils::EnableBitMaskOperators : public std::true_type {}; +template<> struct utils::EnableIntegerOperators + : public std::true_type {}; + #endif diff --git a/libs/filabridge/include/private/filament/DescriptorSets.h b/libs/filabridge/include/private/filament/DescriptorSets.h new file mode 100644 index 00000000000..287702f7d0e --- /dev/null +++ b/libs/filabridge/include/private/filament/DescriptorSets.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DESCRIPTORSETS_H +#define TNT_FILAMENT_DESCRIPTORSETS_H + +#include + +#include + +#include + +#include + +namespace filament::descriptor_sets { + +backend::DescriptorSetLayout const& getPostProcessLayout() noexcept; +backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept; +backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept; +backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept; + +backend::DescriptorSetLayout const& getPerViewLayout() noexcept; // @deprecated +backend::DescriptorSetLayout getPerViewDescriptorSetLayout( + MaterialDomain domain, + UserVariantFilterMask variantFilter, + bool isLit, + ReflectionMode reflectionMode, + RefractionMode refractionMode) noexcept; + +utils::CString getDescriptorName( + filament::DescriptorSetBindingPoints set, + backend::descriptor_binding_t binding) noexcept; + +} // namespace filament::descriptor_sets + + +#endif //TNT_FILAMENT_DESCRIPTORSETS_H diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 4a8c042e756..7fa90c22d8d 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -30,34 +30,45 @@ namespace filament { static constexpr size_t POST_PROCESS_VARIANT_BITS = 1; static constexpr size_t POST_PROCESS_VARIANT_COUNT = (1u << POST_PROCESS_VARIANT_BITS); static constexpr size_t POST_PROCESS_VARIANT_MASK = POST_PROCESS_VARIANT_COUNT - 1; + enum class PostProcessVariant : uint8_t { OPAQUE, TRANSLUCENT }; -// Binding points for uniform buffers -enum class UniformBindingPoints : uint8_t { - PER_VIEW = 0, // uniforms updated per view - PER_RENDERABLE = 1, // uniforms updated per renderable - PER_RENDERABLE_BONES = 2, // bones data, per renderable - PER_RENDERABLE_MORPHING = 3, // morphing uniform/sampler updated per render primitive - LIGHTS = 4, // lights data array - SHADOW = 5, // punctual shadow data - FROXEL_RECORDS = 6, - FROXELS = 7, - PER_MATERIAL_INSTANCE = 8, // uniforms updates per material - // Update utils::Enum::count<>() below when adding values here - // These are limited by CONFIG_BINDING_COUNT (currently 10) +enum class DescriptorSetBindingPoints : uint8_t { + PER_VIEW = 0, + PER_RENDERABLE = 1, + PER_MATERIAL = 2, +}; + +// binding point for the "per-view" descriptor set +enum class PerViewBindingPoints : uint8_t { + FRAME_UNIFORMS = 0, // uniforms updated per view + LIGHTS = 1, // lights data array + SHADOWS = 2, // punctual shadow data + RECORD_BUFFER = 3, // froxel record buffer + FROXEL_BUFFER = 4, // froxel buffer + SHADOW_MAP = 5, // user defined (1024x1024) DEPTH, array + IBL_DFG_LUT = 6, // user defined (128x128), RGB16F + IBL_SPECULAR = 7, // user defined, user defined, CUBEMAP + SSAO = 8, // variable, RGB8 {AO, [depth]} + SSR = 9, // variable, RGB_11_11_10, mipmapped + STRUCTURE = 10, // variable, DEPTH + FOG = 11 // variable, user defined, CUBEMAP }; -// Binding points for sampler buffers. -enum class SamplerBindingPoints : uint8_t { - PER_VIEW = 0, // samplers updated per view - PER_RENDERABLE_MORPHING = 1, // morphing sampler updated per render primitive - PER_MATERIAL_INSTANCE = 2, // samplers updates per material - PER_RENDERABLE_SKINNING = 3, // bone indices and weights sampler updated per render primitive - // Update utils::Enum::count<>() below when adding values here - // These are limited by CONFIG_SAMPLER_BINDING_COUNT (currently 4) +enum class PerRenderableBindingPoints : uint8_t { + OBJECT_UNIFORMS = 0, // uniforms updated per renderable + BONES_UNIFORMS = 1, + MORPHING_UNIFORMS = 2, + MORPH_TARGET_POSITIONS = 3, + MORPH_TARGET_TANGENTS = 4, + BONES_INDICES_AND_WEIGHTS = 5, +}; + +enum class PerMaterialBindingPoints : uint8_t { + MATERIAL_PARAMS = 0, // uniforms }; enum class ReservedSpecializationConstants : uint8_t { @@ -137,9 +148,14 @@ constexpr uint8_t CONFIG_MAX_STEREOSCOPIC_EYES = 4; } // namespace filament template<> -struct utils::EnableIntegerOperators : public std::true_type {}; +struct utils::EnableIntegerOperators : public std::true_type {}; +template<> +struct utils::EnableIntegerOperators : public std::true_type {}; template<> -struct utils::EnableIntegerOperators : public std::true_type {}; +struct utils::EnableIntegerOperators : public std::true_type {}; +template<> +struct utils::EnableIntegerOperators : public std::true_type {}; + template<> struct utils::EnableIntegerOperators : public std::true_type {}; template<> @@ -147,14 +163,7 @@ struct utils::EnableIntegerOperators : public std::tr template<> struct utils::EnableIntegerOperators : public std::true_type {}; -template<> -inline constexpr size_t utils::Enum::count() { return 9; } -template<> -inline constexpr size_t utils::Enum::count() { return 4; } template<> inline constexpr size_t utils::Enum::count() { return filament::POST_PROCESS_VARIANT_COUNT; } -static_assert(utils::Enum::count() <= filament::backend::CONFIG_UNIFORM_BINDING_COUNT); -static_assert(utils::Enum::count() <= filament::backend::CONFIG_SAMPLER_BINDING_COUNT); - #endif // TNT_FILAMENT_ENGINE_ENUM_H diff --git a/libs/filabridge/include/private/filament/SamplerBindingsInfo.h b/libs/filabridge/include/private/filament/SamplerBindingsInfo.h deleted file mode 100644 index e8f3164eda6..00000000000 --- a/libs/filabridge/include/private/filament/SamplerBindingsInfo.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_FILABRIDGE_SAMPLERBINDINGS_INFO_H -#define TNT_FILABRIDGE_SAMPLERBINDINGS_INFO_H - -#include - -#include - -#include -#include - -#include - -namespace filament { - -// binding information about a sampler group -struct SamplerGroupBindingInfo { - constexpr static uint8_t UNKNOWN_OFFSET = 0xff; - // global binding of this block, or UNKNOWN_OFFSET if not used. - uint8_t bindingOffset = UNKNOWN_OFFSET; - // shader stage flags for samplers in this block - backend::ShaderStageFlags shaderStageFlags = backend::ShaderStageFlags::NONE; - // number of samplers in this block. Can be zero. - uint8_t count = 0; -}; - -// list of binding information for all known binding points -using SamplerGroupBindingInfoList = - std::array()>; - -// map of sampler shader binding to sampler shader name -using SamplerBindingToNameMap = - utils::FixedCapacityVector; - -} // namespace filament - -#endif //TNT_FILABRIDGE_SAMPLERBINDINGS_INFO_H diff --git a/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h b/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h index 03f5b5e82cb..243f2dcfb6c 100644 --- a/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h +++ b/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h @@ -49,17 +49,20 @@ class SamplerInterfaceBlock { using Format = backend::SamplerFormat; using Precision = backend::Precision; using SamplerParams = backend::SamplerParams; + using Binding = backend::descriptor_binding_t; struct SamplerInfo { // NOLINT(cppcoreguidelines-pro-type-member-init) utils::CString name; // name of this sampler utils::CString uniformName; // name of the uniform holding this sampler (needed for glsl/MSL) - uint8_t offset; // offset in "Sampler" of this sampler in the buffer + Binding binding; // binding in the descriptor set Type type; // type of this sampler Format format; // format of this sampler Precision precision; // precision of this sampler bool multisample; // multisample capable }; + using SamplerInfoList = utils::FixedCapacityVector; + class Builder { public: Builder(); @@ -72,6 +75,7 @@ class SamplerInterfaceBlock { struct ListEntry { // NOLINT(cppcoreguidelines-pro-type-member-init) std::string_view name; // name of this sampler + Binding binding; // binding in the descriptor set Type type; // type of this sampler Format format; // format of this sampler Precision precision; // precision of this sampler @@ -84,7 +88,7 @@ class SamplerInterfaceBlock { Builder& stageFlags(backend::ShaderStageFlags stageFlags); // Add a sampler - Builder& add(std::string_view samplerName, Type type, Format format, + Builder& add(std::string_view samplerName, Binding binding, Type type, Format format, Precision precision = Precision::MEDIUM, bool multisample = false) noexcept; @@ -109,7 +113,7 @@ class SamplerInterfaceBlock { size_t getSize() const noexcept { return mSamplersInfoList.size(); } // list of information records for each sampler - utils::FixedCapacityVector const& getSamplerInfoList() const noexcept { + SamplerInfoList const& getSamplerInfoList() const noexcept { return mSamplersInfoList; } diff --git a/libs/filabridge/src/DescriptorSets.cpp b/libs/filabridge/src/DescriptorSets.cpp new file mode 100644 index 00000000000..f0a138bc5cd --- /dev/null +++ b/libs/filabridge/src/DescriptorSets.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "private/filament/DescriptorSets.h" + +#include + +#include + +#include + +#include +#include + +#include +#include + +namespace filament::descriptor_sets { + +using namespace backend; + +static DescriptorSetLayout const postProcessDescriptorSetLayout{ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + +PerViewBindingPoints::FRAME_UNIFORMS, + DescriptorFlags::NONE, 0 }, + }}; + +static DescriptorSetLayout const depthVariantDescriptorSetLayout{ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + +PerViewBindingPoints::FRAME_UNIFORMS, + DescriptorFlags::NONE, 0 }, + }}; + +static DescriptorSetLayout const ssrVariantDescriptorSetLayout{ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + +PerViewBindingPoints::FRAME_UNIFORMS, + DescriptorFlags::NONE, 0 }, + { + DescriptorType::SAMPLER, + ShaderStageFlags::FRAGMENT, + +PerViewBindingPoints::SSR, + DescriptorFlags::NONE, 0 }, + { + DescriptorType::SAMPLER, + ShaderStageFlags::FRAGMENT, + +PerViewBindingPoints::STRUCTURE, + DescriptorFlags::NONE, 0 }, + }}; + +static DescriptorSetLayout perViewDescriptorSetLayout = {{ + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS, DescriptorFlags::NONE, 0 }, + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::LIGHTS, DescriptorFlags::NONE, 0 }, + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOWS, DescriptorFlags::NONE, 0 }, + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::RECORD_BUFFER, DescriptorFlags::NONE, 0 }, + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FROXEL_BUFFER, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SHADOW_MAP, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::STRUCTURE, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSAO, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_DFG_LUT, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::IBL_SPECULAR, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::SSR, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG, DescriptorFlags::NONE, 0 }, +}}; + +static DescriptorSetLayout perRenderableDescriptorSetLayout = {{ + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET, 0 }, + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET, 0 }, + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS, DescriptorFlags::NONE, 0 }, + { DescriptorType::SAMPLER, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, DescriptorFlags::NONE, 0 }, +}}; + +DescriptorSetLayout const& getPostProcessLayout() noexcept { + return postProcessDescriptorSetLayout; +} + +DescriptorSetLayout const& getDepthVariantLayout() noexcept { + return depthVariantDescriptorSetLayout; +} + +DescriptorSetLayout const& getSsrVariantLayout() noexcept { + return ssrVariantDescriptorSetLayout; +} + +DescriptorSetLayout const& getPerViewLayout() noexcept { + return perViewDescriptorSetLayout; +} + +DescriptorSetLayout const& getPerRenderableLayout() noexcept { + return perRenderableDescriptorSetLayout; +} + +utils::CString getDescriptorName(DescriptorSetBindingPoints set, + descriptor_binding_t binding) noexcept { + using namespace std::literals; + constexpr const std::string_view set0[] = { + "FrameUniforms"sv, + "LightsUniforms"sv, + "ShadowUniforms"sv, + "FroxelRecordUniforms"sv, + "FroxelsUniforms"sv, + "sampler0_shadowMap"sv, + "sampler0_iblDFG"sv, + "sampler0_iblSpecular"sv, + "sampler0_ssao"sv, + "sampler0_ssr"sv, + "sampler0_structure"sv, + "sampler0_fog"sv, + }; + constexpr const std::string_view set1[] = { + "ObjectUniforms"sv, + "BonesUniforms"sv, + "MorphingUniforms"sv, + "sampler1_positions"sv, + "sampler1_tangents"sv, + "sampler1_indicesAndWeights"sv, + }; + + switch (set) { + case DescriptorSetBindingPoints::PER_VIEW: { + assert_invariant(binding < perViewDescriptorSetLayout.bindings.size()); + std::string_view const& s = set0[binding]; + return { s.data(), s.size() }; + } + case DescriptorSetBindingPoints::PER_RENDERABLE: { + assert_invariant(binding < perRenderableDescriptorSetLayout.bindings.size()); + std::string_view const& s = set1[binding]; + return { s.data(), s.size() }; + } + case DescriptorSetBindingPoints::PER_MATERIAL: { + assert_invariant(binding < 1); + return "MaterialParams"; + } + } +} + +DescriptorSetLayout getPerViewDescriptorSetLayout( + MaterialDomain domain, + UserVariantFilterMask variantFilter, + bool isLit, + ReflectionMode reflectionMode, + RefractionMode refractionMode) noexcept { + switch (domain) { + case MaterialDomain::SURFACE: { + // + // CAVEAT: The logic here must match MaterialBuilder::checkMaterialLevelFeatures() + // + auto layout = perViewDescriptorSetLayout; + // remove descriptors not needed for unlit materials + if (!isLit) { + layout.bindings.erase( + std::remove_if(layout.bindings.begin(), layout.bindings.end(), + [](auto const& entry) { + return entry.binding == PerViewBindingPoints::IBL_DFG_LUT || + entry.binding == PerViewBindingPoints::IBL_SPECULAR; + }), + layout.bindings.end()); + } + // remove descriptors not needed for SSRs + if (reflectionMode != ReflectionMode::SCREEN_SPACE && + refractionMode != RefractionMode::SCREEN_SPACE) { + layout.bindings.erase( + std::remove_if(layout.bindings.begin(), layout.bindings.end(), + [](auto const& entry) { + return entry.binding == PerViewBindingPoints::SSR; + }), + layout.bindings.end()); + + } + // remove fog descriptor if filtered out + if (variantFilter & (UserVariantFilterMask)UserVariantFilterBit::FOG) { + layout.bindings.erase( + std::remove_if(layout.bindings.begin(), layout.bindings.end(), + [](auto const& entry) { + return entry.binding == PerViewBindingPoints::FOG; + }), + layout.bindings.end()); + } + return layout; + } + case MaterialDomain::POST_PROCESS: + return descriptor_sets::getPostProcessLayout(); + case MaterialDomain::COMPUTE: + // TODO: what's the layout for compute? + return descriptor_sets::getPostProcessLayout(); + } +} + +} // namespace filament::descriptor_sets diff --git a/libs/filabridge/src/SamplerInterfaceBlock.cpp b/libs/filabridge/src/SamplerInterfaceBlock.cpp index 43ad966ea48..1d1f4c387ff 100644 --- a/libs/filabridge/src/SamplerInterfaceBlock.cpp +++ b/libs/filabridge/src/SamplerInterfaceBlock.cpp @@ -16,11 +16,17 @@ #include "private/filament/SamplerInterfaceBlock.h" + +#include + #include -#include +#include +#include +#include #include +#include #include using namespace utils; @@ -43,11 +49,12 @@ SamplerInterfaceBlock::Builder::stageFlags(backend::ShaderStageFlags stageFlags) } SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add( - std::string_view samplerName, Type type, Format format, + std::string_view samplerName, Binding binding, Type type, Format format, Precision precision, bool multisample) noexcept { mEntries.push_back({ - { samplerName.data(), samplerName.size() }, { }, - uint8_t(mEntries.size()), type, format, precision, multisample }); + { samplerName.data(), samplerName.size() }, // name + { }, // uniform name + binding, type, format, precision, multisample }); return *this; } @@ -58,7 +65,7 @@ SamplerInterfaceBlock SamplerInterfaceBlock::Builder::build() { SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add( std::initializer_list list) noexcept { for (auto& e : list) { - add(e.name, e.type, e.format, e.precision, e.multisample); + add(e.name, e.binding, e.type, e.format, e.precision, e.multisample); } return *this; } @@ -79,15 +86,13 @@ SamplerInterfaceBlock::SamplerInterfaceBlock(Builder const& builder) noexcept auto& samplersInfoList = mSamplersInfoList; - size_t i = 0; for (auto const& e : builder.mEntries) { - assert_invariant(i == e.offset); - SamplerInfo& info = samplersInfoList[i++]; + size_t const i = std::distance(builder.mEntries.data(), &e); + SamplerInfo& info = samplersInfoList[i]; info = e; info.uniformName = generateUniformName(mName.c_str(), e.name.c_str()); - infoMap[{ info.name.data(), info.name.size() }] = info.offset; // info.name.c_str() guaranteed constant + infoMap[{ info.name.data(), info.name.size() }] = i; // info.name.c_str() guaranteed constant } - assert_invariant(i == samplersInfoList.size()); } const SamplerInterfaceBlock::SamplerInfo* SamplerInterfaceBlock::getSamplerInfo( diff --git a/libs/filamat/CMakeLists.txt b/libs/filamat/CMakeLists.txt index 2fc20d7e916..c74ad631fa6 100644 --- a/libs/filamat/CMakeLists.txt +++ b/libs/filamat/CMakeLists.txt @@ -42,7 +42,6 @@ set(COMMON_SRCS src/Includes.cpp src/MaterialBuilder.cpp src/MaterialVariants.cpp - src/SamplerBindingMap.cpp ) # Sources and headers for filamat @@ -84,7 +83,7 @@ include_directories(${CMAKE_BINARY_DIR}) add_library(${TARGET} STATIC ${HDRS} ${PRIVATE_HDRS} ${SRCS}) target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR}) set_target_properties(${TARGET} PROPERTIES FOLDER Libs) -target_link_libraries(${TARGET} shaders filabridge utils smol-v) +target_link_libraries(${TARGET} backend_headers shaders filabridge utils smol-v) # We are being naughty and accessing private headers here # For spirv-tools, we're just following glslang's example diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 3a4a4824e0e..c1e2a7092da 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -57,27 +57,27 @@ namespace msl { // this is only used for MSL using BindingIndexMap = std::unordered_map; -static void collectSibs(const GLSLPostProcessor::Config& config, SibVector& sibs) { - switch (config.domain) { - case MaterialDomain::SURFACE: - UTILS_NOUNROLL - for (uint8_t blockIndex = 0; blockIndex < CONFIG_SAMPLER_BINDING_COUNT; blockIndex++) { - if (blockIndex == SamplerBindingPoints::PER_MATERIAL_INSTANCE) { - continue; - } - auto const* sib = - SibGenerator::getSib((SamplerBindingPoints)blockIndex, config.variant); - if (sib && hasShaderType(sib->getStageFlags(), config.shaderType)) { - sibs.emplace_back(blockIndex, sib); - } - } - case MaterialDomain::POST_PROCESS: - case MaterialDomain::COMPUTE: - break; - } - sibs.emplace_back((uint8_t) SamplerBindingPoints::PER_MATERIAL_INSTANCE, - &config.materialInfo->sib); -} +//static void collectSibs(const GLSLPostProcessor::Config& config, SibVector& sibs) { +// switch (config.domain) { +// case MaterialDomain::SURFACE: +// UTILS_NOUNROLL +// for (uint8_t blockIndex = 0; blockIndex < CONFIG_SAMPLER_BINDING_COUNT; blockIndex++) { +// if (blockIndex == SamplerBindingPoints::PER_MATERIAL_INSTANCE) { +// continue; +// } +// auto const* sib = +// SibGenerator::getSib((SamplerBindingPoints)blockIndex, config.variant); +// if (sib && hasShaderType(sib->getStageFlags(), config.shaderType)) { +// sibs.emplace_back(blockIndex, sib); +// } +// } +// case MaterialDomain::POST_PROCESS: +// case MaterialDomain::COMPUTE: +// break; +// } +// sibs.emplace_back((uint8_t) SamplerBindingPoints::PER_MATERIAL_INSTANCE, +// &config.materialInfo->sib); +//} } // namespace msl @@ -225,74 +225,63 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl, // // Which is then bound to the vertex/fragment functions: // constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(27)]] - for (auto [bindingPoint, sib] : sibs) { - const auto& infoList = sib->getSamplerInfoList(); - - // bindingPoint + 1, because the first descriptor set is for uniforms - auto argBufferBuilder = MetalArgumentBuffer::Builder() - .name("spvDescriptorSetBuffer" + std::to_string(int(bindingPoint + 1))); - - for (const auto& info: infoList) { - const std::string name = info.uniformName.c_str(); - argBufferBuilder - .texture(info.offset * 2, name, info.type, info.format, info.multisample) - .sampler(info.offset * 2 + 1, name + "Smplr"); - } - - argumentBuffers.push_back(argBufferBuilder.build()); - - // This MSLResourceBinding is how we control the [[buffer(n)]] binding of the argument - // buffer itself; - MSLResourceBinding argBufferBinding; - // the baseType doesn't matter, but can't be UNKNOWN - argBufferBinding.basetype = SPIRType::BaseType::Float; - argBufferBinding.stage = executionModel; - argBufferBinding.desc_set = bindingPoint + 1; - argBufferBinding.binding = kArgumentBufferBinding; - argBufferBinding.count = 1; - argBufferBinding.msl_buffer = - CodeGenerator::METAL_SAMPLER_GROUP_BINDING_START + bindingPoint; - mslCompiler.add_msl_resource_binding(argBufferBinding); - } - - // Bind push constants to [buffer(26)] - MSLResourceBinding pushConstantBinding; - // the baseType doesn't matter, but can't be UNKNOWN - pushConstantBinding.basetype = SPIRType::BaseType::Struct; - pushConstantBinding.stage = executionModel; - pushConstantBinding.desc_set = kPushConstDescSet; - pushConstantBinding.binding = kPushConstBinding; - pushConstantBinding.count = 1; - pushConstantBinding.msl_buffer = 26; - mslCompiler.add_msl_resource_binding(pushConstantBinding); - - auto updateResourceBindingDefault = [executionModel, &mslCompiler](const auto& resource) { - auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet); - auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding); - MSLResourceBinding newBinding; - newBinding.basetype = SPIRType::BaseType::Void; - newBinding.stage = executionModel; - newBinding.desc_set = set; - newBinding.binding = binding; - newBinding.count = 1; - newBinding.msl_texture = - newBinding.msl_sampler = - newBinding.msl_buffer = binding; - mslCompiler.add_msl_resource_binding(newBinding); - }; - - auto uniformResources = mslCompiler.get_shader_resources(); - for (const auto& resource : uniformResources.uniform_buffers) { - updateResourceBindingDefault(resource); - } - auto ssboResources = mslCompiler.get_shader_resources(); - for (const auto& resource : ssboResources.storage_buffers) { - updateResourceBindingDefault(resource); - } +// for (auto [bindingPoint, sib] : sibs) { +// const auto& infoList = sib->getSamplerInfoList(); +// +// // bindingPoint + 1, because the first descriptor set is for uniforms +// auto argBufferBuilder = MetalArgumentBuffer::Builder() +// .name("spvDescriptorSetBuffer" + std::to_string(int(bindingPoint + 1))); +// +// for (const auto& info: infoList) { +// const std::string name = info.uniformName.c_str(); +// argBufferBuilder +// .texture(info.offset * 2, name, info.type, info.format, info.multisample) +// .sampler(info.offset * 2 + 1, name + "Smplr"); +// } +// +// argumentBuffers.push_back(argBufferBuilder.build()); +// +// // This MSLResourceBinding is how we control the [[buffer(n)]] binding of the argument +// // buffer itself; +// MSLResourceBinding argBufferBinding; +// // the baseType doesn't matter, but can't be UNKNOWN +// argBufferBinding.basetype = SPIRType::BaseType::Float; +// argBufferBinding.stage = executionModel; +// argBufferBinding.desc_set = bindingPoint + 1; +// argBufferBinding.binding = kArgumentBufferBinding; +// argBufferBinding.count = 1; +// argBufferBinding.msl_buffer = +// CodeGenerator::METAL_SAMPLER_GROUP_BINDING_START + bindingPoint; +// mslCompiler.add_msl_resource_binding(argBufferBinding); +// } +// +// auto updateResourceBindingDefault = [executionModel, &mslCompiler](const auto& resource) { +// auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet); +// auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding); +// MSLResourceBinding newBinding; +// newBinding.basetype = SPIRType::BaseType::Void; +// newBinding.stage = executionModel; +// newBinding.desc_set = set; +// newBinding.binding = binding; +// newBinding.count = 1; +// newBinding.msl_texture = +// newBinding.msl_sampler = +// newBinding.msl_buffer = binding; +// mslCompiler.add_msl_resource_binding(newBinding); +// }; +// +// auto uniformResources = mslCompiler.get_shader_resources(); +// for (const auto& resource : uniformResources.uniform_buffers) { +// updateResourceBindingDefault(resource); +// } +// auto ssboResources = mslCompiler.get_shader_resources(); +// for (const auto& resource : ssboResources.storage_buffers) { +// updateResourceBindingDefault(resource); +// } // Descriptor set 0 is uniforms. The add_discrete_descriptor_set call here prevents the uniforms // from becoming argument buffers. - mslCompiler.add_discrete_descriptor_set(0); +// mslCompiler.add_discrete_descriptor_set(0); *outMsl = mslCompiler.compile(); if (minifier) { @@ -394,7 +383,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co fixupClipDistance(*internalConfig.spirvOutput, config); if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); - msl::collectSibs(config, sibs); +// msl::collectSibs(config, sibs); spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, sibs, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); @@ -483,7 +472,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); - msl::collectSibs(config, sibs); +// msl::collectSibs(config, sibs); spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, sibs, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); @@ -524,7 +513,7 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); - msl::collectSibs(config, sibs); +// msl::collectSibs(config, sibs); spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, sibs, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 11d539870a8..b6b7404e6d8 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -39,25 +39,35 @@ #include "eiff/SimpleFieldChunk.h" #include "eiff/DictionaryTextChunk.h" #include "eiff/DictionarySpirvChunk.h" -#include "eiff/DictionaryMetalLibraryChunk.h" #include #include #include #include +#include +#include +#include #include +#include +#include +#include #include #include #include #include #include +#include #include +#include #include #include +#include +#include + namespace filamat { @@ -578,12 +588,13 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept { // Build the per-material sampler block and uniform block. SamplerInterfaceBlock::Builder sbb; BufferInterfaceBlock::Builder ibb; - for (size_t i = 0, c = mParameterCount; i < c; i++) { + // sampler bindings start at 1, 0 is the ubo + for (size_t i = 0, binding = 1, c = mParameterCount; i < c; i++) { auto const& param = mParameters[i]; assert_invariant(!param.isSubpass()); if (param.isSampler()) { sbb.add({ param.name.data(), param.name.size() }, - param.samplerType, param.format, param.precision, param.multisample); + binding++, param.samplerType, param.format, param.precision, param.multisample); } else if (param.isUniform()) { ibb.add({{{ param.name.data(), param.name.size() }, uint32_t(param.size == 1u ? 0u : param.size), param.uniformType, @@ -907,15 +918,16 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector> list({ - { UniformBindingPoints::PER_VIEW, - extractUniforms(UibGenerator::getPerViewUib()) }, - { UniformBindingPoints::PER_RENDERABLE, - extractUniforms(UibGenerator::getPerRenderableUib()) }, - { UniformBindingPoints::PER_MATERIAL_INSTANCE, - extractUniforms(info.uib) }, - }); - // FIXME: don't hardcode this - auto& uniforms = list[1].second; + FixedCapacityVector> list({ + { 0, "FrameUniforms", extractUniforms(UibGenerator::getPerViewUib()) }, + { 1, "ObjectUniforms", extractUniforms(UibGenerator::getPerRenderableUib()) }, + { 2, "MaterialParams", extractUniforms(info.uib) }, + }); + auto& uniforms = std::get<2>(list[1]); uniforms.clear(); uniforms.reserve(6); uniforms.push_back({ @@ -1491,37 +1496,22 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& container.push(std::move(attributes)); } - // TODO: currently, the feature level used is determined by the material because we - // don't have "feature level" variants. In other words, a feature level 0 material - // won't work with a feature level 1 engine. However, we do embed the feature level 1 - // meta-data, as it should. - - if (info.featureLevel <= FeatureLevel::FEATURE_LEVEL_1) { - // note: this chunk is only needed for OpenGL backends, which don't all support layout(binding=) - FixedCapacityVector> list = { - { PerViewUib::_name, UniformBindingPoints::PER_VIEW }, - { PerRenderableUib::_name, UniformBindingPoints::PER_RENDERABLE }, - { LightsUib::_name, UniformBindingPoints::LIGHTS }, - { ShadowUib::_name, UniformBindingPoints::SHADOW }, - { FroxelRecordUib::_name, UniformBindingPoints::FROXEL_RECORDS }, - { FroxelsUib::_name, UniformBindingPoints::FROXELS }, - { PerRenderableBoneUib::_name, UniformBindingPoints::PER_RENDERABLE_BONES }, - { PerRenderableMorphingUib::_name, UniformBindingPoints::PER_RENDERABLE_MORPHING }, - { info.uib.getName(), UniformBindingPoints::PER_MATERIAL_INSTANCE } - }; - container.push(std::move(list)); - } - - // note: this chunk is needed for Vulkan and GL backends. Metal shouldn't need it (but - // still does as of now). - container.push(info.samplerBindings); - - // User Material UIB + // User parameters (UBO) container.push(info.uib); - // User Material SIB + // User texture parameters container.push(info.sib); + + backend::DescriptorSetLayout const perViewDescriptorSetLayout = + descriptor_sets::getPerViewDescriptorSetLayout( + mMaterialDomain, mVariantFilter, + info.isLit, info.reflectionMode, info.refractionMode); + + // Descriptor layout and descriptor name/binding mapping + container.push(info.sib, perViewDescriptorSetLayout); + container.push(info.sib, perViewDescriptorSetLayout); + // User constant parameters utils::FixedCapacityVector constantsEntry(mConstants.size()); std::transform(mConstants.begin(), mConstants.end(), constantsEntry.begin(), diff --git a/libs/filamat/src/SamplerBindingMap.cpp b/libs/filamat/src/SamplerBindingMap.cpp deleted file mode 100644 index b2770a20726..00000000000 --- a/libs/filamat/src/SamplerBindingMap.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SamplerBindingMap.h" - -#include "shaders/SibGenerator.h" - -#include - -#include - -#include - -namespace filament { - -using namespace utils; -using namespace backend; - -void SamplerBindingMap::init(MaterialDomain materialDomain, - SamplerInterfaceBlock const& perMaterialSib) { - - assert_invariant(mActiveSamplerCount == 0); - - mSamplerNamesBindingMap.reserve(MAX_SAMPLER_COUNT); - mSamplerNamesBindingMap.resize(MAX_SAMPLER_COUNT); - - // Note: the material variant affects only the sampler types, but cannot affect - // the actual bindings. For this reason it is okay to use the dummyVariant here. - uint8_t offset = 0; - size_t vertexSamplerCount = 0; - size_t fragmentSamplerCount = 0; - - auto processSamplerGroup = [&](SamplerBindingPoints bindingPoint){ - SamplerInterfaceBlock const* const sib = - (bindingPoint == SamplerBindingPoints::PER_MATERIAL_INSTANCE) ? - &perMaterialSib : SibGenerator::getSib(bindingPoint, {}); - if (sib) { - const auto stageFlags = sib->getStageFlags(); - auto const& list = sib->getSamplerInfoList(); - const size_t samplerCount = list.size(); - - if (any(stageFlags & ShaderStageFlags::VERTEX)) { - vertexSamplerCount += samplerCount; - } - if (any(stageFlags & ShaderStageFlags::FRAGMENT)) { - fragmentSamplerCount += samplerCount; - } - - mSamplerBlockOffsets[+bindingPoint] = { offset, stageFlags, uint8_t(samplerCount) }; - for (size_t i = 0; i < samplerCount; i++) { - assert_invariant(mSamplerNamesBindingMap[offset + i].empty()); - mSamplerNamesBindingMap[offset + i] = list[i].uniformName; - } - - offset += samplerCount; - } - }; - - switch(materialDomain) { - case MaterialDomain::SURFACE: - UTILS_NOUNROLL - for (size_t i = 0; i < Enum::count(); i++) { - processSamplerGroup((SamplerBindingPoints)i); - } - break; - case MaterialDomain::POST_PROCESS: - case MaterialDomain::COMPUTE: - processSamplerGroup(SamplerBindingPoints::PER_MATERIAL_INSTANCE); - break; - } - - mActiveSamplerCount = offset; - - // we shouldn't be using more total samplers than supported - assert_invariant(vertexSamplerCount + fragmentSamplerCount <= MAX_SAMPLER_COUNT); - - // Here we cannot check for overflow for a given feature level because we don't know - // what feature level the backend will support. We only know the feature level declared - // by the material. However, we can at least assert for the highest feature level. - - constexpr size_t MAX_VERTEX_SAMPLER_COUNT = - backend::FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3].MAX_VERTEX_SAMPLER_COUNT; - - assert_invariant(vertexSamplerCount <= MAX_VERTEX_SAMPLER_COUNT); - - constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = - backend::FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3].MAX_FRAGMENT_SAMPLER_COUNT; - - assert_invariant(fragmentSamplerCount <= MAX_FRAGMENT_SAMPLER_COUNT); -} - -} // namespace filament diff --git a/libs/filamat/src/SamplerBindingMap.h b/libs/filamat/src/SamplerBindingMap.h deleted file mode 100644 index d76078138d6..00000000000 --- a/libs/filamat/src/SamplerBindingMap.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H -#define TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H - -#include - -#include - -namespace filament { - -class SamplerInterfaceBlock; - -/* - * SamplerBindingMap maps filament's (BindingPoints, offset) to a global offset. - * This global offset is used in shaders to set the `layout(binding=` of each sampler. - * - * It also keeps a map of global offsets to the sampler name in the shader. - * - * SamplerBindingMap is flattened into the material file and used on the filament side to - * create the backend's programs. - */ -class SamplerBindingMap { -public: - - using SamplerGroupBindingInfo = SamplerGroupBindingInfo; - - // Initializes the SamplerBindingMap. - // Assigns a range of finalized binding points to each sampler block. - // If a per-material SIB is provided, then material samplers are also inserted (always at the - // end). - void init(MaterialDomain materialDomain, - SamplerInterfaceBlock const& perMaterialSib); - - SamplerGroupBindingInfo const& getSamplerGroupBindingInfo( - SamplerBindingPoints bindingPoint) const noexcept { - return mSamplerBlockOffsets[+bindingPoint]; - } - - // Gets the global offset of the first sampler in the given sampler block. - inline uint8_t getBlockOffset(SamplerBindingPoints bindingPoint) const noexcept { - assert_invariant(mSamplerBlockOffsets[+bindingPoint].bindingOffset != UNKNOWN_OFFSET); - return getSamplerGroupBindingInfo(bindingPoint).bindingOffset; - } - - size_t getActiveSamplerCount() const noexcept { - return mActiveSamplerCount; - } - - utils::CString const& getSamplerName(size_t binding) const noexcept { - return mSamplerNamesBindingMap[binding]; - } - -private: - constexpr static uint8_t UNKNOWN_OFFSET = SamplerGroupBindingInfo::UNKNOWN_OFFSET; - SamplerGroupBindingInfoList mSamplerBlockOffsets{}; - SamplerBindingToNameMap mSamplerNamesBindingMap{}; - uint8_t mActiveSamplerCount = 0; -}; - -} // namespace filament - -#endif // TNT_FILAMENT_DRIVER_SAMPLERBINDINGMAP_H diff --git a/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp b/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp index 5f92ca7356b..23913f27183 100644 --- a/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp +++ b/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.cpp @@ -17,15 +17,22 @@ #include "filament/MaterialChunkType.h" -#include "../SamplerBindingMap.h" -#include #include -#include #include +#include +#include #include +#include +#include + +#include + +#include #include +#include + using namespace filament; namespace filamat { @@ -62,6 +69,7 @@ void MaterialSamplerInterfaceBlockChunk::flatten(Flattener& f) { f.writeUint64(sibFields.size()); for (auto sInfo: sibFields) { f.writeString(sInfo.name.c_str()); + f.writeUint8(static_cast(sInfo.binding)); f.writeUint8(static_cast(sInfo.type)); f.writeUint8(static_cast(sInfo.format)); f.writeUint8(static_cast(sInfo.precision)); @@ -123,64 +131,16 @@ void MaterialPushConstantParametersChunk::flatten(Flattener& f) { // ------------------------------------------------------------------------------------------------ -MaterialUniformBlockBindingsChunk::MaterialUniformBlockBindingsChunk( - utils::FixedCapacityVector> list) - : Chunk(ChunkType::MaterialUniformBindings), - mBindingList(std::move(list)) { -} - -void MaterialUniformBlockBindingsChunk::flatten(Flattener& f) { - f.writeUint8(mBindingList.size()); - for (auto const& item: mBindingList) { - f.writeString(item.first); - f.writeUint8(uint8_t(item.second)); - } -} - -// ------------------------------------------------------------------------------------------------ - -MaterialSamplerBlockBindingChunk::MaterialSamplerBlockBindingChunk( - SamplerBindingMap const& samplerBindings) - : Chunk(ChunkType::MaterialSamplerBindings), - mSamplerBindings(samplerBindings) { -} - -void MaterialSamplerBlockBindingChunk::flatten(Flattener& f) { - f.writeUint8(utils::Enum::count()); - UTILS_NOUNROLL - for (size_t i = 0; i < utils::Enum::count(); i++) { - SamplerBindingPoints const bindingPoint = (SamplerBindingPoints)i; - auto const& bindingInfo = mSamplerBindings.getSamplerGroupBindingInfo(bindingPoint); - f.writeUint8(bindingInfo.bindingOffset); - f.writeUint8((uint8_t)bindingInfo.shaderStageFlags); - f.writeUint8(bindingInfo.count); - } - f.writeUint8(mSamplerBindings.getActiveSamplerCount()); - UTILS_UNUSED_IN_RELEASE size_t c = 0; - UTILS_NOUNROLL - for (size_t i = 0; i < backend::MAX_SAMPLER_COUNT; i++) { - auto const& uniformName = mSamplerBindings.getSamplerName(i); - if (!uniformName.empty()) { - f.writeUint8((uint8_t)i); - f.writeString(uniformName.c_str()); - c++; - } - } - assert_invariant(c == mSamplerBindings.getActiveSamplerCount()); -} - -// ------------------------------------------------------------------------------------------------ - MaterialBindingUniformInfoChunk::MaterialBindingUniformInfoChunk(Container list) noexcept : Chunk(ChunkType::MaterialBindingUniformInfo), - mBindingUniformInfo(std::move(list)) -{ + mBindingUniformInfo(std::move(list)) { } void MaterialBindingUniformInfoChunk::flatten(Flattener& f) { f.writeUint8(mBindingUniformInfo.size()); - for (auto const& [index, uniforms] : mBindingUniformInfo) { + for (auto const& [index, name, uniforms] : mBindingUniformInfo) { f.writeUint8(uint8_t(index)); + f.writeString({ name.data(), name.size() }); f.writeUint8(uint8_t(uniforms.size())); for (auto const& uniform: uniforms) { f.writeString({ uniform.name.data(), uniform.name.size() }); @@ -207,4 +167,113 @@ void MaterialAttributesInfoChunk::flatten(Flattener& f) { } } +// ------------------------------------------------------------------------------------------------ + +MaterialDescriptorBindingsChuck::MaterialDescriptorBindingsChuck(Container const& sib, + backend::DescriptorSetLayout const& perViewLayout) noexcept + : Chunk(ChunkType::MaterialDescriptorBindingsInfo), + mSamplerInterfaceBlock(sib), + mPerViewLayout(perViewLayout) { +} + +void MaterialDescriptorBindingsChuck::flatten(Flattener& f) { + assert_invariant(sizeof(backend::descriptor_set_t) == sizeof(uint8_t)); + assert_invariant(sizeof(backend::descriptor_binding_t) == sizeof(uint8_t)); + + using namespace backend; + + + // number of descriptor-sets + f.writeUint8(3); + + // set + f.writeUint8(+DescriptorSetBindingPoints::PER_MATERIAL); + + // samplers + 1 descriptor for the UBO + f.writeUint8(mSamplerInterfaceBlock.getSize() + 1); + + // our UBO descriptor is always at binding 0 + CString const uboName = + descriptor_sets::getDescriptorName(DescriptorSetBindingPoints::PER_MATERIAL, 0); + f.writeString({ uboName.data(), uboName.size() }); + f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER)); + f.writeUint8(0); + + // all the material's sampler descriptors + for (auto const& entry: mSamplerInterfaceBlock.getSamplerInfoList()) { + f.writeString({ entry.uniformName.data(), entry.uniformName.size() }); + f.writeUint8(uint8_t(DescriptorType::SAMPLER)); + f.writeUint8(entry.binding); + } + + // set + f.writeUint8(+DescriptorSetBindingPoints::PER_RENDERABLE); + f.writeUint8(descriptor_sets::getPerRenderableLayout().bindings.size()); + for (auto const& entry: descriptor_sets::getPerRenderableLayout().bindings) { + auto const& name = descriptor_sets::getDescriptorName( + DescriptorSetBindingPoints::PER_RENDERABLE, entry.binding); + f.writeString({ name.data(), name.size() }); + f.writeUint8(uint8_t(entry.type)); + f.writeUint8(entry.binding); + } + + // set + f.writeUint8(+DescriptorSetBindingPoints::PER_VIEW); + f.writeUint8(mPerViewLayout.bindings.size()); + for (auto const& entry: mPerViewLayout.bindings) { + auto const& name = descriptor_sets::getDescriptorName( + DescriptorSetBindingPoints::PER_VIEW, entry.binding); + f.writeString({ name.data(), name.size() }); + f.writeUint8(uint8_t(entry.type)); + f.writeUint8(entry.binding); + } +} + +// ------------------------------------------------------------------------------------------------ + +MaterialDescriptorSetLayoutChunk::MaterialDescriptorSetLayoutChunk(Container const& sib, + backend::DescriptorSetLayout const& perViewLayout) noexcept + : Chunk(ChunkType::MaterialDescriptorSetLayoutInfo), + mSamplerInterfaceBlock(sib), + mPerViewLayout(perViewLayout) { +} + +void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) { + assert_invariant(sizeof(backend::descriptor_set_t) == sizeof(uint8_t)); + assert_invariant(sizeof(backend::descriptor_binding_t) == sizeof(uint8_t)); + + using namespace backend; + + // samplers + 1 descriptor for the UBO + f.writeUint8(mSamplerInterfaceBlock.getSize() + 1); + + // our UBO descriptor is always at binding 0 + f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER)); + f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)); + f.writeUint8(0); + f.writeUint8(uint8_t(DescriptorFlags::NONE)); + f.writeUint16(0); + + // all the material's sampler descriptors + for (auto const& entry: mSamplerInterfaceBlock.getSamplerInfoList()) { + f.writeUint8(uint8_t(DescriptorType::SAMPLER)); + f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)); + f.writeUint8(entry.binding); + f.writeUint8(uint8_t(DescriptorFlags::NONE)); + f.writeUint16(0); + } + + // samplers + 1 descriptor for the UBO + f.writeUint8(mPerViewLayout.bindings.size()); + + // all the material's sampler descriptors + for (auto const& entry: mPerViewLayout.bindings) { + f.writeUint8(uint8_t(entry.type)); + f.writeUint8(uint8_t(entry.stageFlags)); + f.writeUint8(entry.binding); + f.writeUint8(uint8_t(entry.flags)); + f.writeUint16(entry.count); + } +} + } // namespace filamat diff --git a/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.h b/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.h index 5faee0147a9..cbb3a85446c 100644 --- a/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.h +++ b/libs/filamat/src/eiff/MaterialInterfaceBlockChunk.h @@ -21,12 +21,18 @@ #include +#include #include +#include #include +#include +#include + +#include + namespace filament { -class SamplerBindingMap; class SamplerInterfaceBlock; class BufferInterfaceBlock; struct SubpassInfo; @@ -104,59 +110,63 @@ class MaterialPushConstantParametersChunk final : public Chunk { // ------------------------------------------------------------------------------------------------ -class MaterialUniformBlockBindingsChunk final : public Chunk { - using Container = utils::FixedCapacityVector< - std::pair>; +class MaterialBindingUniformInfoChunk final : public Chunk { + using Container = FixedCapacityVector>; public: - explicit MaterialUniformBlockBindingsChunk(Container list); - ~MaterialUniformBlockBindingsChunk() final = default; + explicit MaterialBindingUniformInfoChunk(Container list) noexcept; + ~MaterialBindingUniformInfoChunk() final = default; private: - void flatten(Flattener&) final; + void flatten(Flattener &) final; - Container mBindingList; + Container mBindingUniformInfo; }; // ------------------------------------------------------------------------------------------------ -class MaterialSamplerBlockBindingChunk final : public Chunk { +class MaterialAttributesInfoChunk final : public Chunk { + using Container = FixedCapacityVector>; public: - explicit MaterialSamplerBlockBindingChunk(filament::SamplerBindingMap const& samplerBindings); - ~MaterialSamplerBlockBindingChunk() final = default; + explicit MaterialAttributesInfoChunk(Container list) noexcept; + ~MaterialAttributesInfoChunk() final = default; private: void flatten(Flattener &) final; - filament::SamplerBindingMap const& mSamplerBindings; + Container mAttributeInfo; }; // ------------------------------------------------------------------------------------------------ -class MaterialBindingUniformInfoChunk final : public Chunk { - using Container = FixedCapacityVector< - std::pair>; +class MaterialDescriptorBindingsChuck final : public Chunk { + using Container = filament::SamplerInterfaceBlock; public: - explicit MaterialBindingUniformInfoChunk(Container list) noexcept; - ~MaterialBindingUniformInfoChunk() final = default; + explicit MaterialDescriptorBindingsChuck(Container const& sib, + filament::backend::DescriptorSetLayout const& perViewLayout) noexcept; + ~MaterialDescriptorBindingsChuck() final = default; private: - void flatten(Flattener &) final; + void flatten(Flattener&) final; - Container mBindingUniformInfo; + Container const& mSamplerInterfaceBlock; + filament::backend::DescriptorSetLayout mPerViewLayout; }; // ------------------------------------------------------------------------------------------------ -class MaterialAttributesInfoChunk final : public Chunk { - using Container = FixedCapacityVector>; +class MaterialDescriptorSetLayoutChunk final : public Chunk { + using Container = filament::SamplerInterfaceBlock; public: - explicit MaterialAttributesInfoChunk(Container list) noexcept; - ~MaterialAttributesInfoChunk() final = default; + explicit MaterialDescriptorSetLayoutChunk(Container const& sib, + filament::backend::DescriptorSetLayout const& perViewLayout) noexcept; + ~MaterialDescriptorSetLayoutChunk() final = default; private: - void flatten(Flattener &) final; + void flatten(Flattener&) final; - Container mAttributeInfo; + Container const& mSamplerInterfaceBlock; + filament::backend::DescriptorSetLayout mPerViewLayout; }; } // namespace filamat diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 75d2b940d8c..e8a7b3bfcda 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -21,6 +21,8 @@ #include "generated/shaders.h" +#include + #include #include @@ -594,17 +596,33 @@ const char* CodeGenerator::getUniformPrecisionQualifier(UniformType type, Precis utils::io::sstream& CodeGenerator::generateBuffers(utils::io::sstream& out, MaterialInfo::BufferContainer const& buffers) const { - uint32_t binding = 0; + for (auto const* buffer : buffers) { - generateBufferInterfaceBlock(out, ShaderStage::COMPUTE, binding, *buffer); - binding++; + + // FIXME: we need to get the bindings for the SSBOs and that will depend on the samplers + backend::descriptor_binding_t binding = 0; + + if (mTargetApi == TargetApi::OPENGL) { + // For OpenGL, the set is not used bug the binding must be unique. + binding = getUniqueSsboBindingPoint(); + } + generateBufferInterfaceBlock(out, ShaderStage::COMPUTE, + DescriptorSetBindingPoints::PER_MATERIAL, binding, *buffer); } return out; } io::sstream& CodeGenerator::generateUniforms(io::sstream& out, ShaderStage stage, - UniformBindingPoints binding, const BufferInterfaceBlock& uib) const { - return generateBufferInterfaceBlock(out, stage, +binding, uib); + filament::DescriptorSetBindingPoints set, + filament::backend::descriptor_binding_t binding, + const BufferInterfaceBlock& uib) const { + + if (mTargetApi == TargetApi::OPENGL) { + // For OpenGL, the set is not used bug the binding must be unique. + binding = getUniqueUboBindingPoint(); + } + + return generateBufferInterfaceBlock(out, stage, set, binding, uib); } io::sstream& CodeGenerator::generateInterfaceFields(io::sstream& out, @@ -659,7 +677,9 @@ io::sstream& CodeGenerator::generateUboAsPlainUniforms(io::sstream& out, ShaderS } io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, ShaderStage stage, - uint32_t binding, const BufferInterfaceBlock& uib) const { + filament::DescriptorSetBindingPoints set, + filament::backend::descriptor_binding_t binding, + const BufferInterfaceBlock& uib) const { if (uib.isEmptyForFeatureLevel(mFeatureLevel)) { return out; } @@ -679,28 +699,19 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade blockName.front() = char(std::toupper((unsigned char)blockName.front())); instanceName.front() = char(std::tolower((unsigned char)instanceName.front())); - auto metalBufferBindingOffset = 0; - switch (uib.getTarget()) { - case BufferInterfaceBlock::Target::UNIFORM: - metalBufferBindingOffset = METAL_UNIFORM_BUFFER_BINDING_START; - break; - case BufferInterfaceBlock::Target::SSBO: - metalBufferBindingOffset = METAL_SSBO_BINDING_START; - break; - } - out << "\nlayout("; if (mTargetLanguage == TargetLanguage::SPIRV || mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) { switch (mTargetApi) { case TargetApi::METAL: - out << "binding = " << metalBufferBindingOffset + binding << ", "; + case TargetApi::VULKAN: + out << "set = " << +set << ", binding = " << +binding << ", "; break; case TargetApi::OPENGL: // GLSL 4.5 / ESSL 3.1 require the 'binding' layout qualifier - case TargetApi::VULKAN: - out << "binding = " << binding << ", "; + // in the GLSL 4.5 / ESSL 3.1 case, the set is not used and binding is unique + out << "binding = " << +binding << ", "; break; case TargetApi::ALL: @@ -754,15 +765,14 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade return out; } -io::sstream& CodeGenerator::generateSamplers( - io::sstream& out, SamplerBindingPoints bindingPoint, uint8_t firstBinding, - const SamplerInterfaceBlock& sib) const { - auto const& infos = sib.getSamplerInfoList(); - if (infos.empty()) { +io::sstream& CodeGenerator::generateSamplers(utils::io::sstream& out, + filament::DescriptorSetBindingPoints set, + filament::SamplerInterfaceBlock::SamplerInfoList const& list) const { + if (list.empty()) { return out; } - for (auto const& info : infos) { + for (auto const& info : list) { auto type = info.type; if (type == SamplerType::SAMPLER_EXTERNAL && mShaderModel != ShaderModel::MOBILE) { // we're generating the shader for the desktop, where we assume external textures @@ -772,27 +782,27 @@ io::sstream& CodeGenerator::generateSamplers( char const* const typeName = getSamplerTypeName(type, info.format, info.multisample); char const* const precision = getPrecisionQualifier(info.precision); if (mTargetLanguage == TargetLanguage::SPIRV) { - const uint32_t bindingIndex = (uint32_t) firstBinding + info.offset; switch (mTargetApi) { - // For Vulkan, we place uniforms in set 0 (the default set) and samplers in set 1. This - // allows the sampler bindings to live in a separate "namespace" that starts at zero. // Note that the set specifier is not covered by the desktop GLSL spec, including // recent versions. It is only documented in the GL_KHR_vulkan_glsl extension. case TargetApi::VULKAN: - out << "layout(binding = " << bindingIndex << ", set = 1) "; + out << "layout(binding = " << +info.binding << ", set = " << +set << ") "; break; // For Metal, each sampler group gets its own descriptor set, each of which will // become an argument buffer. The first descriptor set is reserved for uniforms, // hence the +1 here. case TargetApi::METAL: - out << "layout(binding = " << (uint32_t) info.offset - << ", set = " << (uint32_t) bindingPoint + 1 << ") "; + out << "layout(binding = " << +info.binding << ", set = " << +set << ") "; break; - default: case TargetApi::OPENGL: - out << "layout(binding = " << bindingIndex << ") "; + // GLSL 4.5 / ESSL 3.1 require the 'binding' layout qualifier + out << "layout(binding = " << getUniqueSamplerBindingPoint() << ") "; + break; + + case TargetApi::ALL: + // should not happen break; } } diff --git a/libs/filamat/src/shaders/CodeGenerator.h b/libs/filamat/src/shaders/CodeGenerator.h index 233243f348c..424371d9750 100644 --- a/libs/filamat/src/shaders/CodeGenerator.h +++ b/libs/filamat/src/shaders/CodeGenerator.h @@ -19,6 +19,7 @@ #include "MaterialInfo.h" +#include "UibGenerator.h" #include @@ -42,6 +43,8 @@ #include #include +#include + namespace filamat { class UTILS_PRIVATE CodeGenerator { @@ -123,8 +126,14 @@ class UTILS_PRIVATE CodeGenerator { // generate samplers utils::io::sstream& generateSamplers(utils::io::sstream& out, - filament::SamplerBindingPoints bindingPoint, uint8_t firstBinding, - const filament::SamplerInterfaceBlock& sib) const; + filament::DescriptorSetBindingPoints set, + filament::SamplerInterfaceBlock::SamplerInfoList const& list) const; + + utils::io::sstream& generateSamplers(utils::io::sstream& out, + filament::DescriptorSetBindingPoints set, + const filament::SamplerInterfaceBlock& sib) const { + return generateSamplers(out, set, sib.getSamplerInfoList()); + } // generate subpass static utils::io::sstream& generateSubpass(utils::io::sstream& out, @@ -132,7 +141,9 @@ class UTILS_PRIVATE CodeGenerator { // generate uniforms utils::io::sstream& generateUniforms(utils::io::sstream& out, ShaderStage stage, - filament::UniformBindingPoints binding, const filament::BufferInterfaceBlock& uib) const; + filament::DescriptorSetBindingPoints set, + filament::backend::descriptor_binding_t binding, + const filament::BufferInterfaceBlock& uib) const; // generate buffers utils::io::sstream& generateBuffers(utils::io::sstream& out, @@ -140,7 +151,9 @@ class UTILS_PRIVATE CodeGenerator { // generate an interface block utils::io::sstream& generateBufferInterfaceBlock(utils::io::sstream& out, ShaderStage stage, - uint32_t binding, const filament::BufferInterfaceBlock& uib) const; + filament::DescriptorSetBindingPoints set, + filament::backend::descriptor_binding_t binding, + const filament::BufferInterfaceBlock& uib) const; // generate material properties getters static utils::io::sstream& generateMaterialProperty(utils::io::sstream& out, @@ -176,6 +189,18 @@ class UTILS_PRIVATE CodeGenerator { static constexpr uint32_t METAL_SAMPLER_GROUP_BINDING_START = 27u; static constexpr uint32_t METAL_SSBO_BINDING_START = 0; + uint32_t getUniqueSamplerBindingPoint() const noexcept { + return mUniqueSamplerBindingPoint++; + } + + uint32_t getUniqueUboBindingPoint() const noexcept { + return mUniqueUboBindingPoint++; + } + + uint32_t getUniqueSsboBindingPoint() const noexcept { + return mUniqueSsboBindingPoint++; + } + private: filament::backend::Precision getDefaultPrecision(ShaderStage stage) const; filament::backend::Precision getDefaultUniformPrecision() const; @@ -219,6 +244,9 @@ class UTILS_PRIVATE CodeGenerator { TargetApi mTargetApi; TargetLanguage mTargetLanguage; FeatureLevel mFeatureLevel; + mutable uint32_t mUniqueSamplerBindingPoint = 0; + mutable uint32_t mUniqueUboBindingPoint = 0; + mutable uint32_t mUniqueSsboBindingPoint = 0; }; } // namespace filamat diff --git a/libs/filamat/src/shaders/MaterialInfo.h b/libs/filamat/src/shaders/MaterialInfo.h index 4979bed5c3c..4324d1a24fd 100644 --- a/libs/filamat/src/shaders/MaterialInfo.h +++ b/libs/filamat/src/shaders/MaterialInfo.h @@ -19,8 +19,6 @@ #include -#include "../SamplerBindingMap.h" - #include #include @@ -66,7 +64,6 @@ struct UTILS_PUBLIC MaterialInfo { filament::BufferInterfaceBlock uib; filament::SamplerInterfaceBlock sib; filament::SubpassInfo subpass; - filament::SamplerBindingMap samplerBindings; filament::ShaderQuality quality; filament::backend::FeatureLevel featureLevel; filament::backend::StereoscopicType stereoscopicType; diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index 6ad24c01481..ccedf738bd6 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -16,21 +16,30 @@ #include "ShaderGenerator.h" +#include "CodeGenerator.h" +#include "SibGenerator.h" +#include "UibGenerator.h" + #include +#include #include #include -#include +#include -#include "backend/DriverEnums.h" -#include "filamat/MaterialBuilder.h" -#include "CodeGenerator.h" -#include "SibGenerator.h" -#include "UibGenerator.h" +#include + +#include +#include +#include +#include #include +#include +#include + namespace filamat { using namespace filament; @@ -443,42 +452,45 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel, // uniforms cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FRAME_UNIFORMS, + UibGenerator::getPerViewUib()); cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib()); + DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::OBJECT_UNIFORMS, + UibGenerator::getPerRenderableUib()); const bool litVariants = material.isLit || material.hasShadowMultiplier; if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) { cg.generateUniforms(vs, ShaderStage::FRAGMENT, - UniformBindingPoints::SHADOW, UibGenerator::getShadowUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::SHADOWS, + UibGenerator::getShadowUib()); } if (hasSkinningOrMorphing(variant, featureLevel)) { cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_RENDERABLE_BONES, + DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::BONES_UNIFORMS, UibGenerator::getPerRenderableBonesUib()); - cg.generateSamplers(vs, SamplerBindingPoints::PER_RENDERABLE_SKINNING, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_RENDERABLE_SKINNING), - SibGenerator::getPerRenderPrimitiveBonesSib(variant)); cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_RENDERABLE_MORPHING, + DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::MORPHING_UNIFORMS, UibGenerator::getPerRenderableMorphingUib()); - - cg.generateSamplers(vs, SamplerBindingPoints::PER_RENDERABLE_MORPHING, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_RENDERABLE_MORPHING), - SibGenerator::getPerRenderPrimitiveMorphingSib(variant)); + cg.generateSamplers(vs, DescriptorSetBindingPoints::PER_RENDERABLE, + SibGenerator::getPerRenderableSib(variant)); } cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib); + DescriptorSetBindingPoints::PER_MATERIAL, + +PerMaterialBindingPoints::MATERIAL_PARAMS, + material.uib); CodeGenerator::generateSeparator(vs); // TODO: should we generate per-view SIB in the vertex shader? - cg.generateSamplers(vs, SamplerBindingPoints::PER_MATERIAL_INSTANCE, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE), - material.sib); + cg.generateSamplers(vs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib); // shader code CodeGenerator::generateCommon(vs, ShaderStage::VERTEX); @@ -498,7 +510,7 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel, MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage, MaterialBuilder::FeatureLevel featureLevel, MaterialInfo const& material, const filament::Variant variant, - Interpolation interpolation) const noexcept { + Interpolation interpolation, UserVariantFilterMask variantFilter) const noexcept { assert_invariant(filament::Variant::isValid(variant)); assert_invariant(mMaterialDomain != MaterialBuilder::MaterialDomain::COMPUTE); @@ -548,43 +560,77 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel, // uniforms and samplers cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FRAME_UNIFORMS, + UibGenerator::getPerViewUib()); cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::PER_RENDERABLE, UibGenerator::getPerRenderableUib()); + DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::OBJECT_UNIFORMS, + UibGenerator::getPerRenderableUib()); if (variant.hasDynamicLighting()) { cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::LIGHTS, UibGenerator::getLightsUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::LIGHTS, + UibGenerator::getLightsUib()); } bool const litVariants = material.isLit || material.hasShadowMultiplier; if (litVariants && filament::Variant::isShadowReceiverVariant(variant)) { cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::SHADOW, UibGenerator::getShadowUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::SHADOWS, + UibGenerator::getShadowUib()); } if (variant.hasDynamicLighting()) { cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::FROXEL_RECORDS, UibGenerator::getFroxelRecordUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::RECORD_BUFFER, + UibGenerator::getFroxelRecordUib()); + cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::FROXELS, UibGenerator::getFroxelsUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FROXEL_BUFFER, + UibGenerator::getFroxelsUib()); } cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib); + DescriptorSetBindingPoints::PER_MATERIAL, + +PerMaterialBindingPoints::MATERIAL_PARAMS, + material.uib); CodeGenerator::generateSeparator(fs); - if (featureLevel >= FeatureLevel::FEATURE_LEVEL_1) { // FIXME: generate only what we need - cg.generateSamplers(fs, SamplerBindingPoints::PER_VIEW, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_VIEW), - SibGenerator::getPerViewSib(variant)); + if (featureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + + // We need to filter out all the descriptors not included in the "resolved" layout below + backend::DescriptorSetLayout const perViewDescriptorSetLayout = + descriptor_sets::getPerViewDescriptorSetLayout( + mMaterialDomain, variantFilter, + material.isLit, material.reflectionMode, material.refractionMode); + + // this is the list of samplers we need to filter + auto list = SibGenerator::getPerViewSib(variant).getSamplerInfoList(); + + // remove all the samplers that are not included in the descriptor-set layout + list.erase( + std::remove_if(list.begin(), list.end(), + [&perViewDescriptorSetLayout](auto const& entry) { + auto pos = std::find_if( + perViewDescriptorSetLayout.bindings.begin(), + perViewDescriptorSetLayout.bindings.end(), + [&entry](const auto& item) { + return item.binding == entry.binding; + }); + return pos == perViewDescriptorSetLayout.bindings.end(); + }), list.end()); + + cg.generateSamplers(fs, DescriptorSetBindingPoints::PER_VIEW, list); } - cg.generateSamplers(fs, SamplerBindingPoints::PER_MATERIAL_INSTANCE, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE), - material.sib); + cg.generateSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib); fs << "float filament_lodBias;\n"; @@ -648,14 +694,16 @@ std::string ShaderGenerator::createComputeProgram(filament::backend::ShaderModel CodeGenerator::generateCommonTypes(s, ShaderStage::COMPUTE); cg.generateUniforms(s, ShaderStage::COMPUTE, - UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FRAME_UNIFORMS, + UibGenerator::getPerViewUib()); cg.generateUniforms(s, ShaderStage::COMPUTE, - UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib); + DescriptorSetBindingPoints::PER_MATERIAL, + +PerMaterialBindingPoints::MATERIAL_PARAMS, + material.uib); - cg.generateSamplers(s, SamplerBindingPoints::PER_MATERIAL_INSTANCE, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE), - material.sib); + cg.generateSamplers(s, DescriptorSetBindingPoints::PER_MATERIAL, material.sib); // generate SSBO cg.generateBuffers(s, material.buffers); @@ -696,14 +744,16 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel sm, generatePostProcessMaterialVariantDefines(vs, PostProcessVariant(variantKey)); cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FRAME_UNIFORMS, + UibGenerator::getPerViewUib()); cg.generateUniforms(vs, ShaderStage::VERTEX, - UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib); + DescriptorSetBindingPoints::PER_MATERIAL, + +PerMaterialBindingPoints::MATERIAL_PARAMS, + material.uib); - cg.generateSamplers(vs, SamplerBindingPoints::PER_MATERIAL_INSTANCE, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE), - material.sib); + cg.generateSamplers(vs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib); CodeGenerator::generatePostProcessCommon(vs, ShaderStage::VERTEX); CodeGenerator::generatePostProcessGetters(vs, ShaderStage::VERTEX); @@ -735,14 +785,16 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm, } cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::PER_VIEW, UibGenerator::getPerViewUib()); + DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FRAME_UNIFORMS, + UibGenerator::getPerViewUib()); cg.generateUniforms(fs, ShaderStage::FRAGMENT, - UniformBindingPoints::PER_MATERIAL_INSTANCE, material.uib); + DescriptorSetBindingPoints::PER_MATERIAL, + +PerMaterialBindingPoints::MATERIAL_PARAMS, + material.uib); - cg.generateSamplers(fs, SamplerBindingPoints::PER_MATERIAL_INSTANCE, - material.samplerBindings.getBlockOffset(SamplerBindingPoints::PER_MATERIAL_INSTANCE), - material.sib); + cg.generateSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib); // subpass CodeGenerator::generateSubpass(fs, material.subpass); diff --git a/libs/filamat/src/shaders/ShaderGenerator.h b/libs/filamat/src/shaders/ShaderGenerator.h index 1b419f3b06c..55afe0dfa0b 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.h +++ b/libs/filamat/src/shaders/ShaderGenerator.h @@ -20,16 +20,24 @@ #include "MaterialInfo.h" +#include "UibGenerator.h" + #include #include +#include #include +#include + #include #include -#include +#include + +#include +#include namespace filamat { @@ -61,7 +69,8 @@ class ShaderGenerator { MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage, MaterialBuilder::FeatureLevel featureLevel, MaterialInfo const& material, filament::Variant variant, - filament::Interpolation interpolation) const noexcept; + filament::Interpolation interpolation, + filament::UserVariantFilterMask variantFilter) const noexcept; std::string createComputeProgram(filament::backend::ShaderModel shaderModel, MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage, diff --git a/libs/filamat/src/shaders/SibGenerator.cpp b/libs/filamat/src/shaders/SibGenerator.cpp index b03a6f3035e..ec07d9b186f 100644 --- a/libs/filamat/src/shaders/SibGenerator.cpp +++ b/libs/filamat/src/shaders/SibGenerator.cpp @@ -21,6 +21,10 @@ #include "private/filament/SamplerInterfaceBlock.h" #include "private/filament/SibStructs.h" +#include + +#include + namespace filament { SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexcept { @@ -37,52 +41,42 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce // // For the SSR (reflections) SamplerInterfaceBlock, only two samplers are ever used, for this // reason we name them "unused*" to ensure we're not using them by mistake (type/format don't - // matter). This will not affect SamplerBindingMap because it always uses the default variant, - // and so the correct information will be stored in the material file. + // matter). static SamplerInterfaceBlock const sibPcf{ SamplerInterfaceBlock::Builder() - .name("Light") + .name("sampler0") .stageFlags(backend::ShaderStageFlags::FRAGMENT) - .add( {{ "shadowMap", Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM }, - { "iblDFG", Type::SAMPLER_2D, Format::FLOAT, Precision::MEDIUM }, - { "iblSpecular", Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }, - { "ssao", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, - { "ssr", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, - { "structure", Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }, - { "fog", Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }} + .add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM }, + { "iblDFG", +PerViewBindingPoints::IBL_DFG_LUT, Type::SAMPLER_2D, Format::FLOAT, Precision::MEDIUM }, + { "iblSpecular", +PerViewBindingPoints::IBL_SPECULAR, Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }, + { "ssao", +PerViewBindingPoints::SSAO, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, + { "ssr", +PerViewBindingPoints::SSR, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, + { "structure", +PerViewBindingPoints::STRUCTURE, Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }, + { "fog", +PerViewBindingPoints::FOG, Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }} ) .build() }; static SamplerInterfaceBlock const sibVsm{ SamplerInterfaceBlock::Builder() - .name("Light") + .name("sampler0") .stageFlags(backend::ShaderStageFlags::FRAGMENT) - .add( {{ "shadowMap", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH }, - { "iblDFG", Type::SAMPLER_2D, Format::FLOAT, Precision::MEDIUM }, - { "iblSpecular", Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }, - { "ssao", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, - { "ssr", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, - { "structure", Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }, - { "fog", Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }} + .add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH }, + { "iblDFG", +PerViewBindingPoints::IBL_DFG_LUT, Type::SAMPLER_2D, Format::FLOAT, Precision::MEDIUM }, + { "iblSpecular", +PerViewBindingPoints::IBL_SPECULAR, Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }, + { "ssao", +PerViewBindingPoints::SSAO, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, + { "ssr", +PerViewBindingPoints::SSR, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::MEDIUM }, + { "structure", +PerViewBindingPoints::STRUCTURE, Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }, + { "fog", +PerViewBindingPoints::FOG, Type::SAMPLER_CUBEMAP, Format::FLOAT, Precision::MEDIUM }} ) .build() }; static SamplerInterfaceBlock const sibSsr{ SamplerInterfaceBlock::Builder() - .name("Light") + .name("sampler0") .stageFlags(backend::ShaderStageFlags::FRAGMENT) - .add( {{ "unused0" }, - { "unused1" }, - { "unused2" }, - { "unused3" }, - { "ssr", Type::SAMPLER_2D, Format::FLOAT, Precision::MEDIUM }, - { "structure", Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }, - { "unused5" }} + .add( {{ "ssr", +PerViewBindingPoints::SSR, Type::SAMPLER_2D, Format::FLOAT, Precision::MEDIUM }, + { "structure", +PerViewBindingPoints::STRUCTURE, Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }} ) .build() }; - assert_invariant(sibPcf.getSize() == PerViewSib::SAMPLER_COUNT); - assert_invariant(sibVsm.getSize() == PerViewSib::SAMPLER_COUNT); - assert_invariant(sibSsr.getSize() == PerViewSib::SAMPLER_COUNT); - if (Variant::isSSRVariant(variant)) { return sibSsr; } else if (Variant::isVSMVariant(variant)) { @@ -92,43 +86,28 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce } } -SamplerInterfaceBlock const& SibGenerator::getPerRenderPrimitiveMorphingSib(Variant) noexcept { +SamplerInterfaceBlock const& SibGenerator::getPerRenderableSib(Variant) noexcept { using Type = SamplerInterfaceBlock::Type; using Format = SamplerInterfaceBlock::Format; using Precision = SamplerInterfaceBlock::Precision; static SamplerInterfaceBlock const sib = SamplerInterfaceBlock::Builder() - .name("MorphTargetBuffer") - .stageFlags(backend::ShaderStageFlags::VERTEX) - .add({ { "positions", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH }, - { "tangents", Type::SAMPLER_2D_ARRAY, Format::INT, Precision::HIGH }}) - .build(); - - return sib; -} - -SamplerInterfaceBlock const& SibGenerator::getPerRenderPrimitiveBonesSib(Variant variant) noexcept { - using Type = SamplerInterfaceBlock::Type; - using Format = SamplerInterfaceBlock::Format; - using Precision = SamplerInterfaceBlock::Precision; - - static SamplerInterfaceBlock sib = SamplerInterfaceBlock::Builder() - .name("BonesBuffer") + .name("sampler1") .stageFlags(backend::ShaderStageFlags::VERTEX) - .add({{"indicesAndWeights", Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }}) + .add({ {"positions", +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH }, + {"tangents", +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS, Type::SAMPLER_2D_ARRAY, Format::INT, Precision::HIGH }, + {"indicesAndWeights", +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH }}) .build(); return sib; } -SamplerInterfaceBlock const* SibGenerator::getSib(SamplerBindingPoints bindingPoint, Variant variant) noexcept { - switch (bindingPoint) { - case SamplerBindingPoints::PER_VIEW: +SamplerInterfaceBlock const* SibGenerator::getSib(DescriptorSetBindingPoints set, Variant variant) noexcept { + switch (set) { + case DescriptorSetBindingPoints::PER_VIEW: return &getPerViewSib(variant); - case SamplerBindingPoints::PER_RENDERABLE_MORPHING: - return &getPerRenderPrimitiveMorphingSib(variant); - case SamplerBindingPoints::PER_RENDERABLE_SKINNING: - return &getPerRenderPrimitiveBonesSib(variant); + case DescriptorSetBindingPoints::PER_RENDERABLE: + return &getPerRenderableSib(variant); default: return nullptr; } diff --git a/libs/filamat/src/shaders/SibGenerator.h b/libs/filamat/src/shaders/SibGenerator.h index eb874653c50..b510d02d308 100644 --- a/libs/filamat/src/shaders/SibGenerator.h +++ b/libs/filamat/src/shaders/SibGenerator.h @@ -30,9 +30,8 @@ class SamplerInterfaceBlock; class SibGenerator { public: static SamplerInterfaceBlock const& getPerViewSib(Variant variant) noexcept; - static SamplerInterfaceBlock const& getPerRenderPrimitiveMorphingSib(Variant variant) noexcept; - static SamplerInterfaceBlock const& getPerRenderPrimitiveBonesSib(Variant variant) noexcept; - static SamplerInterfaceBlock const* getSib(filament::SamplerBindingPoints bindingPoint, Variant variant) noexcept; + static SamplerInterfaceBlock const& getPerRenderableSib(Variant variant) noexcept; + static SamplerInterfaceBlock const* getSib(filament::DescriptorSetBindingPoints bindingPoint, Variant variant) noexcept; // When adding a sampler block here, make sure to also update // FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed }; diff --git a/libs/filamat/src/shaders/UibGenerator.cpp b/libs/filamat/src/shaders/UibGenerator.cpp index 37361440274..9d4d360b1a1 100644 --- a/libs/filamat/src/shaders/UibGenerator.cpp +++ b/libs/filamat/src/shaders/UibGenerator.cpp @@ -22,10 +22,70 @@ #include #include +#include + +#include + namespace filament { using namespace backend; +BufferInterfaceBlock const& UibGenerator::get(UibGenerator::Ubo ubo) noexcept { + assert_invariant(ubo != Ubo::MaterialParams); + switch (ubo) { + case Ubo::FrameUniforms: + return getPerViewUib(); + case Ubo::ObjectUniforms: + return getPerRenderableUib(); + case Ubo::BonesUniforms: + return getPerRenderableBonesUib(); + case Ubo::MorphingUniforms: + return getPerRenderableMorphingUib(); + case Ubo::LightsUniforms: + return getLightsUib(); + case Ubo::ShadowUniforms: + return getShadowUib(); + case Ubo::FroxelRecordUniforms: + return getFroxelRecordUib(); + case Ubo::FroxelsUniforms: + return getFroxelsUib(); + case Ubo::MaterialParams: + abort(); + } +} + +UibGenerator::Binding UibGenerator::getBinding(UibGenerator::Ubo ubo) noexcept { + switch (ubo) { + case Ubo::FrameUniforms: + return { +DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FRAME_UNIFORMS }; + case Ubo::ObjectUniforms: + return { +DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::OBJECT_UNIFORMS }; + case Ubo::BonesUniforms: + return { +DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::BONES_UNIFORMS }; + case Ubo::MorphingUniforms: + return { +DescriptorSetBindingPoints::PER_RENDERABLE, + +PerRenderableBindingPoints::MORPHING_UNIFORMS }; + case Ubo::LightsUniforms: + return { +DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::LIGHTS }; + case Ubo::ShadowUniforms: + return { +DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::SHADOWS }; + case Ubo::FroxelRecordUniforms: + return { +DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::RECORD_BUFFER }; + case Ubo::FroxelsUniforms: + return { +DescriptorSetBindingPoints::PER_VIEW, + +PerViewBindingPoints::FROXEL_BUFFER }; + case Ubo::MaterialParams: + return { +DescriptorSetBindingPoints::PER_MATERIAL, + +PerMaterialBindingPoints::MATERIAL_PARAMS }; + } +} + static_assert(CONFIG_MAX_SHADOW_CASCADES == 4, "Changing CONFIG_MAX_SHADOW_CASCADES affects PerView size and breaks materials."); diff --git a/libs/filamat/src/shaders/UibGenerator.h b/libs/filamat/src/shaders/UibGenerator.h index a39c44c1230..a2250bdad98 100644 --- a/libs/filamat/src/shaders/UibGenerator.h +++ b/libs/filamat/src/shaders/UibGenerator.h @@ -17,12 +17,50 @@ #ifndef TNT_FILAMAT_UIBGENERATOR_H #define TNT_FILAMAT_UIBGENERATOR_H +#include + +#include + +#include + +#include +#include + namespace filament { class BufferInterfaceBlock; class UibGenerator { public: + // tag to represent a generated ubo. + enum class Ubo : uint8_t { + FrameUniforms, // uniforms updated per view + ObjectUniforms, // uniforms updated per renderable + BonesUniforms, // bones data, per renderable + MorphingUniforms, // morphing uniform/sampler updated per render primitive + LightsUniforms, // lights data array + ShadowUniforms, // punctual shadow data + FroxelRecordUniforms, // froxel records + FroxelsUniforms, // froxels + MaterialParams, // material instance ubo + // Update utils::Enum::count<>() below when adding values here + // These are limited by CONFIG_BINDING_COUNT (currently 10) + // When adding an UBO here, make sure to also update + // MaterialBuilder::writeCommonChunks() if needed + }; + + struct Binding { + backend::descriptor_set_t set; + backend::descriptor_binding_t binding; + }; + + // return the BufferInterfaceBlock for the given UBO tag + static BufferInterfaceBlock const& get(Ubo ubo) noexcept; + + // return the {set, binding } for the given UBO tag + static Binding getBinding(Ubo ubo) noexcept; + + // deprecate these... static BufferInterfaceBlock const& getPerViewUib() noexcept; static BufferInterfaceBlock const& getPerRenderableUib() noexcept; static BufferInterfaceBlock const& getLightsUib() noexcept; @@ -31,10 +69,15 @@ class UibGenerator { static BufferInterfaceBlock const& getPerRenderableMorphingUib() noexcept; static BufferInterfaceBlock const& getFroxelRecordUib() noexcept; static BufferInterfaceBlock const& getFroxelsUib() noexcept; - // When adding an UBO here, make sure to also update - // MaterialBuilder::writeCommonChunks() if needed }; } // namespace filament +template<> +struct utils::EnableIntegerOperators : public std::true_type {}; + +template<> +inline constexpr size_t utils::Enum::count() { return 9; } + + #endif // TNT_FILAMAT_UIBGENERATOR_H diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 31bbc169f7c..2b5af9a9e54 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -650,9 +650,6 @@ void FAssetLoader::createPrimitives(const cgltf_node* node, const char* name, if (outputPrim.indices) { fAsset->mIndexBuffers.push_back(outputPrim.indices); } - if (outputPrim.targets) { - fAsset->mMorphTargetBuffers.push_back(outputPrim.targets); - } } } else { // Create a Filament VertexBuffer and IndexBuffer for this prim if we haven't @@ -695,11 +692,11 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const // glTF spec says that all primitives must have the same number of morph targets. const cgltf_size numMorphTargets = inputPrim ? inputPrim->targets_count : 0; RenderableManager::Builder builder(primitiveCount); - builder.morphing(numMorphTargets); // For each prim, create a Filament VertexBuffer, IndexBuffer, and MaterialInstance. // The VertexBuffer and IndexBuffer objects are cached for possible re-use, but MaterialInstance // is not. + size_t morphingVertexCount = 0; for (cgltf_size index = 0; index < primitiveCount; ++index, ++outputPrim, ++inputPrim) { RenderableManager::PrimitiveType primType; if (!getPrimitiveType(inputPrim->type, &primType)) { @@ -740,8 +737,57 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const builder.geometry(index, primType, outputPrim->vertices, outputPrim->indices); if (numMorphTargets) { - assert_invariant(outputPrim->targets); - builder.morphing(0, index, outputPrim->targets); + outputPrim->morphTargetOffset = morphingVertexCount; // FIXME: can I do that here? + builder.morphing(0, index, morphingVertexCount, outputPrim->vertices->getVertexCount()); + morphingVertexCount += outputPrim->vertices->getVertexCount(); + } + } + + if (numMorphTargets) { + MorphTargetBuffer* morphTargetBuffer = MorphTargetBuffer::Builder() + .count(numMorphTargets) + .vertexCount(morphingVertexCount) + .build(mEngine); + + fAsset->mMorphTargetBuffers.push_back(morphTargetBuffer); + + builder.morphing(morphTargetBuffer); + + using BufferSlot = FFilamentAsset::ResourceInfo::BufferSlot; + auto slots = &std::get(fAsset->mResourceInfo).mBufferSlots; + + outputPrim = prims.data(); + inputPrim = &mesh->primitives[0]; + for (cgltf_size index = 0; index < primitiveCount; ++index, ++outputPrim, ++inputPrim) { + outputPrim->morphTargetBuffer = morphTargetBuffer; + + UTILS_UNUSED_IN_RELEASE cgltf_accessor const* previous = nullptr; + for (int tindex = 0; tindex < numMorphTargets; ++tindex) { + const cgltf_morph_target& inTarget = inputPrim->targets[tindex]; + for (cgltf_size aindex = 0; aindex < inTarget.attributes_count; ++aindex) { + const cgltf_attribute& attribute = inTarget.attributes[aindex]; + const cgltf_accessor* accessor = attribute.data; + const cgltf_attribute_type atype = attribute.type; + if (atype == cgltf_attribute_type_position) { + // All position attributes must have the same number of components. + assert_invariant(!previous || previous->type == accessor->type); + previous = accessor; + + BufferSlot& slot = (*slots)[outputPrim->slotIndices[tindex]]; + + assert_invariant(!slot.vertexBuffer); + assert_invariant(!slot.indexBuffer); + assert_invariant(outputPrim->morphTargetBuffer); + + slot.morphTargetBuffer = outputPrim->morphTargetBuffer; + slot.morphTargetOffset = outputPrim->morphTargetOffset; + slot.morphTargetCount = outputPrim->vertices->getVertexCount(); + slot.bufferIndex = tindex; + + break; + } + } + } } } @@ -753,7 +799,7 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const nm.setMorphTargetNames(nm.getInstance(entity), std::move(morphTargetNames)); if (node->skin) { - builder.skinning(node->skin->joints_count); + builder.skinning(node->skin->joints_count); } // Per the spec, glTF models must have valid mix / max annotations for position attributes. @@ -825,7 +871,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na // request from Google. // Create a little lambda that appends to the asset's vertex buffer slots. - auto slots = &std::get(fAsset->mResourceInfo).mBufferSlots; + auto* const slots = &std::get(fAsset->mResourceInfo).mBufferSlots; auto addBufferSlot = [slots](FFilamentAsset::ResourceInfo::BufferSlot entry) { slots->push_back(entry); }; @@ -1096,13 +1142,8 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na } if (targetsCount > 0) { - MorphTargetBuffer* targets = MorphTargetBuffer::Builder() - .vertexCount(vertexCount) - .count(targetsCount) - .build(mEngine); - outPrim->targets = targets; - fAsset->mMorphTargetBuffers.push_back(targets); UTILS_UNUSED_IN_RELEASE cgltf_accessor const* previous = nullptr; + outPrim->slotIndices.resize(targetsCount); for (int tindex = 0; tindex < targetsCount; ++tindex) { const cgltf_morph_target& inTarget = inPrim.targets[tindex]; for (cgltf_size aindex = 0; aindex < inTarget.attributes_count; ++aindex) { @@ -1114,8 +1155,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na assert_invariant(!previous || previous->type == accessor->type); previous = accessor; BufferSlot slot = { accessor }; - slot.morphTargetBuffer = targets; - slot.bufferIndex = tindex; + outPrim->slotIndices[tindex] = slots->size(); addBufferSlot(slot); break; } diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index 6ee6a9d0590..45360cfaddc 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -96,7 +96,9 @@ struct Primitive { IndexBuffer* indices = nullptr; Aabb aabb; // object-space bounding box UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes) - MorphTargetBuffer* targets = nullptr; + MorphTargetBuffer* morphTargetBuffer = nullptr; + uint32_t morphTargetOffset; + std::vector slotIndices; }; using MeshCache = utils::FixedCapacityVector>; @@ -329,6 +331,8 @@ struct FFilamentAsset : public FilamentAsset { VertexBuffer* vertexBuffer; IndexBuffer* indexBuffer; MorphTargetBuffer* morphTargetBuffer; + uint32_t morphTargetOffset; + uint32_t morphTargetCount; }; std::vector mBufferSlots; diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 9b077e94357..6676de31469 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -293,10 +293,13 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine, cgltf_accessor_unpack_floats(accessor, floatsData, floatsCount); if (accessor->type == cgltf_type_vec3) { slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, - (const float3*) floatsData, slot.morphTargetBuffer->getVertexCount()); + (const float3*) floatsData, + slot.morphTargetCount, + slot.morphTargetOffset); } else { slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, - (const float4*) data, slot.morphTargetBuffer->getVertexCount()); + (const float4*) data, slot.morphTargetBuffer->getVertexCount(), + slot.morphTargetOffset); } free(floatsData); continue; @@ -304,11 +307,13 @@ inline void uploadBuffers(FFilamentAsset* asset, Engine& engine, if (accessor->type == cgltf_type_vec3) { slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, (const float3*) data, - slot.morphTargetBuffer->getVertexCount()); + slot.morphTargetCount, + slot.morphTargetOffset); } else { assert_invariant(accessor->type == cgltf_type_vec4); slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, (const float4*) data, - slot.morphTargetBuffer->getVertexCount()); + slot.morphTargetCount, + slot.morphTargetOffset); } } } @@ -694,7 +699,7 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { } auto iter = baseTangents.find(vb); if (iter != baseTangents.end()) { - jobParams.emplace_back(Params {{ prim }, {vb, nullptr, iter->second }}); + jobParams.emplace_back(Params {{ prim }, {vb, nullptr, 0, iter->second }}); } } @@ -707,7 +712,8 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { } for (cgltf_size pindex = 0, pcount = mesh.primitives_count; pindex < pcount; ++pindex) { const cgltf_primitive& prim = mesh.primitives[pindex]; - MorphTargetBuffer* tb = prims[pindex].targets; + MorphTargetBuffer* const tb = prims[pindex].morphTargetBuffer; + uint32_t const morphTargetOffset = prims[pindex].morphTargetOffset; for (cgltf_size tindex = 0, tcount = prim.targets_count; tindex < tcount; ++tindex) { const cgltf_morph_target& target = prim.targets[tindex]; bool hasNormals = false; @@ -719,13 +725,13 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { } hasNormals = true; jobParams.emplace_back(Params { { &prim, (int) tindex }, - { nullptr, tb, (uint8_t) pindex } }); + { nullptr, tb, morphTargetOffset, (uint8_t) pindex } }); break; } // Generate flat normals if necessary. if (!hasNormals && prim.material && !prim.material->unlit) { jobParams.emplace_back(Params { { &prim, (int) tindex }, - { nullptr, tb, (uint8_t) pindex } }); + { nullptr, tb, morphTargetOffset, (uint8_t) pindex } }); } } } @@ -752,7 +758,7 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { } else { assert_invariant(params.context.tb); params.context.tb->setTangentsAt(*mEngine, params.in.morphTargetIndex, - params.out.results, params.out.vertexCount); + params.out.results, params.out.vertexCount, params.context.offset); free(params.out.results); } } diff --git a/libs/gltfio/src/TangentsJob.h b/libs/gltfio/src/TangentsJob.h index 43011e25c58..6aeed1ea3bf 100644 --- a/libs/gltfio/src/TangentsJob.h +++ b/libs/gltfio/src/TangentsJob.h @@ -47,6 +47,7 @@ struct TangentsJob { struct Context { VertexBuffer* const vb; MorphTargetBuffer* const tb; + uint32_t const offset; const uint8_t slot; }; diff --git a/libs/gltfio/src/extended/AssetLoaderExtended.cpp b/libs/gltfio/src/extended/AssetLoaderExtended.cpp index 7cb4fb32309..769379278cd 100644 --- a/libs/gltfio/src/extended/AssetLoaderExtended.cpp +++ b/libs/gltfio/src/extended/AssetLoaderExtended.cpp @@ -545,10 +545,12 @@ bool AssetLoaderExtended::createPrimitive(Input* input, Output* out, assert_invariant(!out->indices || out->indices == slot.indices); out->indices = slot.indices; } - if (slot.target) { - assert_invariant(!out->targets || out->targets == slot.target); - out->targets = slot.target; - } + // FIXME: repair morphing + assert_invariant(!slot.target); +// if (slot.target) { +// assert_invariant(!out->targets || out->targets == slot.target); +// out->targets = slot.target; +// } } outSlots.insert(outSlots.end(), slots.begin(), slots.end()); diff --git a/libs/gltfio/test/gltfio_test.cpp b/libs/gltfio/test/gltfio_test.cpp index 44b7d99e223..239edbaae09 100644 --- a/libs/gltfio/test/gltfio_test.cpp +++ b/libs/gltfio/test/gltfio_test.cpp @@ -228,7 +228,7 @@ TEST_F(glTFIOTest, AnimatedMorphCubeRenderables) { EXPECT_EQ(renderableManager.getMorphTargetCount(inst), 2u); // The 0-th MorphTargetBuffer holds both of the targets - auto const morphTargetBuffer = renderableManager.getMorphTargetBufferAt(inst, 0, 0); + auto const morphTargetBuffer = renderableManager.getMorphTargetBuffer(inst); EXPECT_EQ(morphTargetBuffer->getCount(), 2u); // The number of vertices for the morph target should be the face vertices in a cube => diff --git a/libs/matdbg/src/TextWriter.cpp b/libs/matdbg/src/TextWriter.cpp index fca5bb0ca19..baa189a1b84 100644 --- a/libs/matdbg/src/TextWriter.cpp +++ b/libs/matdbg/src/TextWriter.cpp @@ -223,6 +223,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container) for (uint64_t i = 0; i < sibCount; i++) { CString fieldName; + uint8_t fieldBinding; uint8_t fieldType; uint8_t fieldFormat; uint8_t fieldPrecision; @@ -232,6 +233,10 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container) return false; } + if (!sib.read(&fieldBinding)) { + return false; + } + if (!sib.read(&fieldType)) { return false; } @@ -249,6 +254,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container) text << " " << setw(alignment) << fieldName.c_str() + << setw(shortAlignment) << +fieldBinding << setw(shortAlignment) << toString(SamplerType(fieldType)) << setw(shortAlignment) << toString(Precision(fieldPrecision)) << toString(SamplerFormat(fieldFormat)) diff --git a/libs/utils/include/utils/FixedCapacityVector.h b/libs/utils/include/utils/FixedCapacityVector.h index 1221e7cc326..e45916aed3b 100644 --- a/libs/utils/include/utils/FixedCapacityVector.h +++ b/libs/utils/include/utils/FixedCapacityVector.h @@ -84,7 +84,7 @@ class UTILS_PUBLIC FixedCapacityVector { FixedCapacityVector() = default; explicit FixedCapacityVector(const allocator_type& allocator) noexcept - : mCapacityAllocator({}, allocator) { + : mCapacityAllocator(0, allocator) { } explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type()) diff --git a/libs/utils/src/EntityManagerImpl.h b/libs/utils/src/EntityManagerImpl.h index adf64a80c85..0465dc1cb1a 100644 --- a/libs/utils/src/EntityManagerImpl.h +++ b/libs/utils/src/EntityManagerImpl.h @@ -68,9 +68,6 @@ class UTILS_PRIVATE EntityManagerImpl : public EntityManager { std::lock_guard const lock(mFreeListLock); Entity::Type currentIndex = mCurrentIndex; for (size_t i = 0; i < n; i++) { - // If we have more than a certain number of freed indices, get one from the list. - // this is a trade-off between how often we recycle indices and how large the free list - // can grow. if (UTILS_UNLIKELY(currentIndex >= RAW_INDEX_COUNT || freeList.size() >= MIN_FREE_INDICES)) { // this could only happen if we had gone through all the indices at least once @@ -83,10 +80,6 @@ class UTILS_PRIVATE EntityManagerImpl : public EntityManager { index = freeList.front(); freeList.pop_front(); } else { - // In the common case, we just grab the next index. - // This works only until all indices have been used once, at which point - // we're always in the slower case above. The idea is that we have enough indices - // that it doesn't happen in practice. index = currentIndex++; } entities[i] = Entity{ makeIdentity(gens[index], index) }; diff --git a/samples/hellomorphing.cpp b/samples/hellomorphing.cpp index 6bd418cf43c..3b590e62c6c 100644 --- a/samples/hellomorphing.cpp +++ b/samples/hellomorphing.cpp @@ -202,9 +202,9 @@ int main(int argc, char** argv) { .culling(false) .receiveShadows(false) .castShadows(false) - .morphing(3) - .morphing(0,0,app.mt1, 0, app.mt1->getCount()) - .morphing(0,1,app.mt1, 9, app.mt1->getCount()) + .morphing(app.mt1) + .morphing(0, 0, 0, 9) + .morphing(0, 1, 9, 9) .build(*engine, app.renderable); scene->addEntity(app.renderable); diff --git a/samples/skinningtest.cpp b/samples/skinningtest.cpp index 8ae6d3f9664..e56bfc6c3cf 100644 --- a/samples/skinningtest.cpp +++ b/samples/skinningtest.cpp @@ -528,9 +528,9 @@ int main(int argc, char** argv) { .culling(false) .receiveShadows(false) .castShadows(false) - .morphing(3) - .morphing(0,1,app.mt) - .morphing(0,3,app.mt) + .morphing(app.mt) + .morphing(0,1, 0, 9) + .morphing(0,3, 0, 9) .build(*engine, app.renderables[0]); // renderable 1: attribute bone data definitions skinning @@ -558,9 +558,9 @@ int main(int argc, char** argv) { .castShadows(false) .enableSkinningBuffers(true) .skinning(app.sb, 9, 0) - .morphing(3) - .morphing(0,2,app.mt) - .morphing(0,0,app.mt) + .morphing(app.mt) + .morphing(0, 2, 0, 9) + .morphing(0, 0, 0, 9) .build(*engine, app.renderables[1]); // renderable 2: various ways of skinning definitions @@ -591,8 +591,8 @@ int main(int argc, char** argv) { .boneIndicesAndWeights(1, app.boneDataPerPrimitive) .boneIndicesAndWeights(2, app.boneDataPerPrimitive) - .morphing(3) - .morphing(0, 2, app.mt) + .morphing(app.mt) + .morphing(0, 2, 0, 9) .build(*engine, app.renderables[2]); // renderable 3: combination attribute and advance bone data @@ -620,9 +620,9 @@ int main(int argc, char** argv) { .skinning(app.sb, 9, 0) .boneIndicesAndWeights(1, app.boneDataPerPrimitiveMulti) .boneIndicesAndWeights(2, app.boneDataPerPrimitive) - .morphing(3) - .morphing(0,0,app.mt) - .morphing(0,2,app.mt) + .morphing(app.mt) + .morphing(0, 0, 0, 9) + .morphing(0, 2, 0, 9) .build(*engine, app.renderables[3]); scene->addEntity(app.renderables[0]); diff --git a/shaders/src/ambient_occlusion.fs b/shaders/src/ambient_occlusion.fs index 32f6421986e..54acfd0f8b3 100644 --- a/shaders/src/ambient_occlusion.fs +++ b/shaders/src/ambient_occlusion.fs @@ -35,18 +35,18 @@ float evaluateSSAO(inout SSAOInterpolationCache cache) { // filter. This adds about 2.0ms @ 250MHz on Pixel 4. if (frameUniforms.aoSamplingQualityAndEdgeDistance > 0.0) { - highp vec2 size = vec2(textureSize(light_ssao, 0)); + highp vec2 size = vec2(textureSize(sampler0_ssao, 0)); // Read four AO samples and their depths values #if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER) - vec4 ao = textureGather(light_ssao, vec3(cache.uv, 0.0), 0); - vec4 dg = textureGather(light_ssao, vec3(cache.uv, 0.0), 1); - vec4 db = textureGather(light_ssao, vec3(cache.uv, 0.0), 2); + vec4 ao = textureGather(sampler0_ssao, vec3(cache.uv, 0.0), 0); + vec4 dg = textureGather(sampler0_ssao, vec3(cache.uv, 0.0), 1); + vec4 db = textureGather(sampler0_ssao, vec3(cache.uv, 0.0), 2); #else - vec3 s01 = textureLodOffset(light_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(0, 1)).rgb; - vec3 s11 = textureLodOffset(light_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(1, 1)).rgb; - vec3 s10 = textureLodOffset(light_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(1, 0)).rgb; - vec3 s00 = textureLodOffset(light_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(0, 0)).rgb; + vec3 s01 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(0, 1)).rgb; + vec3 s11 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(1, 1)).rgb; + vec3 s10 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(1, 0)).rgb; + vec3 s00 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 0.0), 0.0, ivec2(0, 0)).rgb; vec4 ao = vec4(s01.r, s11.r, s10.r, s00.r); vec4 dg = vec4(s01.g, s11.g, s10.g, s00.g); vec4 db = vec4(s01.b, s11.b, s10.b, s00.b); @@ -74,7 +74,7 @@ float evaluateSSAO(inout SSAOInterpolationCache cache) { cache.weights = w / (w.x + w.y + w.z + w.w); return dot(ao, cache.weights); } else { - return textureLod(light_ssao, vec3(cache.uv, 0.0), 0.0).r; + return textureLod(sampler0_ssao, vec3(cache.uv, 0.0), 0.0).r; } #else // SSAO is not applied when blending is enabled @@ -160,14 +160,14 @@ float specularAO(float NoV, float visibility, float roughness, const in SSAOInte vec3 bn; if (frameUniforms.aoSamplingQualityAndEdgeDistance > 0.0) { #if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER) - vec4 bnr = textureGather(light_ssao, vec3(cache.uv, 1.0), 0); - vec4 bng = textureGather(light_ssao, vec3(cache.uv, 1.0), 1); - vec4 bnb = textureGather(light_ssao, vec3(cache.uv, 1.0), 2); + vec4 bnr = textureGather(sampler0_ssao, vec3(cache.uv, 1.0), 0); + vec4 bng = textureGather(sampler0_ssao, vec3(cache.uv, 1.0), 1); + vec4 bnb = textureGather(sampler0_ssao, vec3(cache.uv, 1.0), 2); #else - vec3 s01 = textureLodOffset(light_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(0, 1)).rgb; - vec3 s11 = textureLodOffset(light_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(1, 1)).rgb; - vec3 s10 = textureLodOffset(light_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(1, 0)).rgb; - vec3 s00 = textureLodOffset(light_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(0, 0)).rgb; + vec3 s01 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(0, 1)).rgb; + vec3 s11 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(1, 1)).rgb; + vec3 s10 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(1, 0)).rgb; + vec3 s00 = textureLodOffset(sampler0_ssao, vec3(cache.uv, 1.0), 0.0, ivec2(0, 0)).rgb; vec4 bnr = vec4(s01.r, s11.r, s10.r, s00.r); vec4 bng = vec4(s01.g, s11.g, s10.g, s00.g); vec4 bnb = vec4(s01.b, s11.b, s10.b, s00.b); @@ -176,7 +176,7 @@ float specularAO(float NoV, float visibility, float roughness, const in SSAOInte bn.g = dot(bng, cache.weights); bn.b = dot(bnb, cache.weights); } else { - bn = textureLod(light_ssao, vec3(cache.uv, 1.0), 0.0).xyz; + bn = textureLod(sampler0_ssao, vec3(cache.uv, 1.0), 0.0).xyz; } bn = unpackBentNormal(bn); diff --git a/shaders/src/fog.fs b/shaders/src/fog.fs index 17029480ca1..33d99eb803d 100644 --- a/shaders/src/fog.fs +++ b/shaders/src/fog.fs @@ -50,7 +50,7 @@ vec4 fog(vec4 color, highp vec3 view) { // a rigid transform, so we can take the transpose instead of the inverse, and for the // same reason we can use it directly instead of taking the cof() to transform a vector. highp mat3 worldFromUserWorldMatrix = transpose(mat3(frameUniforms.userWorldFromWorldMatrix)); - fogColor *= textureLod(light_fog, worldFromUserWorldMatrix * view, lod).rgb; + fogColor *= textureLod(sampler0_fog, worldFromUserWorldMatrix * view, lod).rgb; } #endif diff --git a/shaders/src/getters.vs b/shaders/src/getters.vs index 024ec9ff411..09569c61554 100644 --- a/shaders/src/getters.vs +++ b/shaders/src/getters.vs @@ -87,7 +87,7 @@ void skinPosition(inout vec3 p, const uvec4 ids, const vec4 weights) { uint pairStop = pairIndex + uint(ids.w - 3u); for (uint i = pairIndex; i < pairStop; ++i) { ivec2 texcoord = ivec2(i % MAX_SKINNING_BUFFER_WIDTH, i / MAX_SKINNING_BUFFER_WIDTH); - vec2 indexWeight = texelFetch(bonesBuffer_indicesAndWeights, texcoord, 0).rg; + vec2 indexWeight = texelFetch(sampler1_indicesAndWeights, texcoord, 0).rg; posSum += mulBoneVertex(p, uint(indexWeight.r)) * indexWeight.g; } p = posSum; @@ -110,7 +110,7 @@ void skinNormal(inout vec3 n, const uvec4 ids, const vec4 weights) { uint pairStop = pairIndex + uint(ids.w - 3u); for (uint i = pairIndex; i < pairStop; i = i + 1u) { ivec2 texcoord = ivec2(i % MAX_SKINNING_BUFFER_WIDTH, i / MAX_SKINNING_BUFFER_WIDTH); - vec2 indexWeight = texelFetch(bonesBuffer_indicesAndWeights, texcoord, 0).rg; + vec2 indexWeight = texelFetch(sampler1_indicesAndWeights, texcoord, 0).rg; normSum += mulBoneNormal(n, uint(indexWeight.r)) * indexWeight.g; } @@ -141,7 +141,7 @@ void skinNormalTangent(inout vec3 n, inout vec3 t, const uvec4 ids, const vec4 w uint pairStop = pairIndex + uint(ids.w - 3u); for (uint i = pairIndex; i < pairStop; i = i + 1u) { ivec2 texcoord = ivec2(i % MAX_SKINNING_BUFFER_WIDTH, i / MAX_SKINNING_BUFFER_WIDTH); - vec2 indexWeight = texelFetch(bonesBuffer_indicesAndWeights, texcoord, 0).rg; + vec2 indexWeight = texelFetch(sampler1_indicesAndWeights, texcoord, 0).rg; normSum += mulBoneNormal(n, uint(indexWeight.r)) * indexWeight.g; tangSum += mulBoneNormal(t, uint(indexWeight.r)) * indexWeight.g; @@ -160,7 +160,7 @@ void morphPosition(inout vec4 p) { float w = morphingUniforms.weights[i][0]; if (w != 0.0) { texcoord.z = i; - p += w * texelFetch(morphTargetBuffer_positions, texcoord, 0); + p += w * texelFetch(sampler1_positions, texcoord, 0); } } } @@ -174,7 +174,7 @@ void morphNormal(inout vec3 n) { float w = morphingUniforms.weights[i][0]; if (w != 0.0) { texcoord.z = i; - ivec4 tangent = texelFetch(morphTargetBuffer_tangents, texcoord, 0); + ivec4 tangent = texelFetch(sampler1_tangents, texcoord, 0); vec3 normal; toTangentFrame(float4(tangent) * (1.0 / 32767.0), normal); n += w * (normal - baseNormal); diff --git a/shaders/src/light_directional.fs b/shaders/src/light_directional.fs index b6d902d7440..9ef63c79e86 100644 --- a/shaders/src/light_directional.fs +++ b/shaders/src/light_directional.fs @@ -57,7 +57,7 @@ void evaluateDirectionalLight(const MaterialInputs material, bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1); if (hasDirectionalShadows && cascadeHasVisibleShadows) { highp vec4 shadowPosition = getShadowPosition(cascade); - visibility = shadow(true, light_shadowMap, cascade, shadowPosition, 0.0); + visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0); // shadow far attenuation highp vec3 v = getWorldPosition() - getWorldCameraPosition(); // (viewFromWorld * v).z == dot(transpose(viewFromWorld), v) diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 5084b10b64d..0d12391078a 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -27,7 +27,7 @@ vec3 decodeDataForIBL(const vec4 data) { vec3 PrefilteredDFG_LUT(float lod, float NoV) { // coord = sqrt(linear_roughness), which is the mapping used by cmgen. - return textureLod(light_iblDFG, vec2(NoV, lod), 0.0).rgb; + return textureLod(sampler0_iblDFG, vec2(NoV, lod), 0.0).rgb; } //------------------------------------------------------------------------------ @@ -63,7 +63,7 @@ vec3 Irradiance_SphericalHarmonics(const vec3 n) { vec3 Irradiance_RoughnessOne(const vec3 n) { // note: lod used is always integer, hopefully the hardware skips tri-linear filtering - return decodeDataForIBL(textureLod(light_iblSpecular, n, frameUniforms.iblRoughnessOneLevel)); + return decodeDataForIBL(textureLod(sampler0_iblSpecular, n, frameUniforms.iblRoughnessOneLevel)); } //------------------------------------------------------------------------------ @@ -88,7 +88,7 @@ vec3 diffuseIrradiance(const vec3 n) { return Irradiance_RoughnessOne(n); } #else - ivec2 s = textureSize(light_iblSpecular, int(frameUniforms.iblRoughnessOneLevel)); + ivec2 s = textureSize(sampler0_iblSpecular, int(frameUniforms.iblRoughnessOneLevel)); float du = 1.0 / float(s.x); float dv = 1.0 / float(s.y); vec3 m0 = normalize(cross(n, vec3(0.0, 1.0, 0.0))); @@ -120,12 +120,12 @@ float perceptualRoughnessToLod(float perceptualRoughness) { vec3 prefilteredRadiance(const vec3 r, float perceptualRoughness) { float lod = perceptualRoughnessToLod(perceptualRoughness); - return decodeDataForIBL(textureLod(light_iblSpecular, r, lod)); + return decodeDataForIBL(textureLod(sampler0_iblSpecular, r, lod)); } vec3 prefilteredRadiance(const vec3 r, float roughness, float offset) { float lod = frameUniforms.iblRoughnessOneLevel * roughness; - return decodeDataForIBL(textureLod(light_iblSpecular, r, lod + offset)); + return decodeDataForIBL(textureLod(sampler0_iblSpecular, r, lod + offset)); } vec3 getSpecularDominantDirection(const vec3 n, const vec3 r, float roughness) { @@ -247,7 +247,7 @@ vec3 isEvaluateSpecularIBL(const PixelParams pixel, const vec3 n, const vec3 v, T *= R; float roughness = pixel.roughness; - float dim = float(textureSize(light_iblSpecular, 0).x); + float dim = float(textureSize(sampler0_iblSpecular, 0).x); float omegaP = (4.0 * PI) / (6.0 * dim * dim); vec3 indirectSpecular = vec3(0.0); @@ -268,7 +268,7 @@ vec3 isEvaluateSpecularIBL(const PixelParams pixel, const vec3 n, const vec3 v, // PDF inverse (we must use D_GGX() here, which is used to generate samples) float ipdf = (4.0 * LoH) / (D_GGX(roughness, NoH, h) * NoH); float mipLevel = prefilteredImportanceSampling(ipdf, omegaP); - vec3 L = decodeDataForIBL(textureLod(light_iblSpecular, l, mipLevel)); + vec3 L = decodeDataForIBL(textureLod(sampler0_iblSpecular, l, mipLevel)); float D = distribution(roughness, NoH, h); float V = visibility(roughness, NoV, NoL); @@ -305,7 +305,7 @@ vec3 isEvaluateDiffuseIBL(const PixelParams pixel, vec3 n, vec3 v) { R[2] = vec3( 0, 0, 1); T *= R; - float dim = float(textureSize(light_iblSpecular, 0).x); + float dim = float(textureSize(sampler0_iblSpecular, 0).x); float omegaP = (4.0 * PI) / (6.0 * dim * dim); vec3 indirectDiffuse = vec3(0.0); @@ -324,7 +324,7 @@ vec3 isEvaluateDiffuseIBL(const PixelParams pixel, vec3 n, vec3 v) { float ipdf = PI / NoL; // we have to bias the mipLevel (+1) to help with very strong highlights float mipLevel = prefilteredImportanceSampling(ipdf, omegaP) + 1.0; - vec3 L = decodeDataForIBL(textureLod(light_iblSpecular, l, mipLevel)); + vec3 L = decodeDataForIBL(textureLod(sampler0_iblSpecular, l, mipLevel)); indirectDiffuse += L; } } @@ -538,7 +538,7 @@ vec3 evaluateRefraction( // distance to camera plane const float invLog2sqrt5 = 0.8614; float lod = max(0.0, (2.0 * log2(perceptualRoughness) + frameUniforms.refractionLodOffset) * invLog2sqrt5); - Ft = textureLod(light_ssr, vec3(p.xy, 0.0), lod).rgb; + Ft = textureLod(sampler0_ssr, vec3(p.xy, 0.0), lod).rgb; #endif // base color changes the amount of light passing through the boundary @@ -580,7 +580,7 @@ void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout v const float invLog2sqrt5 = 0.8614; float d = -mulMat4x4Float3(getViewFromWorldMatrix(), getWorldPosition()).z; float lod = max(0.0, (log2(pixel.roughness / d) + frameUniforms.refractionLodOffset) * invLog2sqrt5); - ssrFr = textureLod(light_ssr, vec3(interpolationCache.uv, 1.0), lod); + ssrFr = textureLod(sampler0_ssr, vec3(interpolationCache.uv, 1.0), lod); } } #else // BLEND_MODE_OPAQUE diff --git a/shaders/src/light_punctual.fs b/shaders/src/light_punctual.fs index 0db8a4ebf23..fa55b057281 100644 --- a/shaders/src/light_punctual.fs +++ b/shaders/src/light_punctual.fs @@ -220,7 +220,7 @@ void evaluatePunctualLights(const MaterialInputs material, vec4(getWorldPosition(), 1.0)); } highp vec4 shadowPosition = getShadowPosition(shadowIndex, light.direction, light.zLight); - visibility = shadow(false, light_shadowMap, shadowIndex, + visibility = shadow(false, sampler0_shadowMap, shadowIndex, shadowPosition, light.zLight); } if (light.contactShadows && visibility > 0.0) { diff --git a/shaders/src/light_reflections.fs b/shaders/src/light_reflections.fs index ce6be8f7baa..b2f9d3cc729 100644 --- a/shaders/src/light_reflections.fs +++ b/shaders/src/light_reflections.fs @@ -220,7 +220,7 @@ vec4 evaluateScreenSpaceReflections(const highp vec3 wsRayDirection) { float maxRayTraceDistance = frameUniforms.ssrDistance; - highp vec2 res = vec2(textureSize(light_structure, 0).xy); + highp vec2 res = vec2(textureSize(sampler0_structure, 0).xy); highp mat4 uvFromViewMatrix = scaleMatrix(res.x, res.y) * frameUniforms.ssrUvFromViewMatrix; @@ -231,7 +231,7 @@ vec4 evaluateScreenSpaceReflections(const highp vec3 wsRayDirection) { highp vec2 hitPixel; // not currently used highp vec3 vsHitPoint; - if (traceScreenSpaceRay(vsOrigin, vsDirection, uvFromViewMatrix, light_structure, + if (traceScreenSpaceRay(vsOrigin, vsDirection, uvFromViewMatrix, sampler0_structure, vsZThickness, nearPlaneZ, stride, jitterFraction, maxSteps, maxRayTraceDistance, hitPixel, vsHitPoint)) { highp vec4 reprojected = mulMat4x4Float3(frameUniforms.ssrReprojection, vsHitPoint); @@ -256,7 +256,7 @@ vec4 evaluateScreenSpaceReflections(const highp vec3 wsRayDirection) { fade *= (1.0 - max(0.0, vsDirection.z)); // we output a premultiplied alpha color because this is going to be mipmapped - Fr = vec4(textureLod(light_ssr, reprojected.xy, 0.0).rgb * fade, fade); + Fr = vec4(textureLod(sampler0_ssr, reprojected.xy, 0.0).rgb * fade, fade); } return Fr; } diff --git a/shaders/src/main.fs b/shaders/src/main.fs index 1bc74372c13..c0d6bdb61f8 100644 --- a/shaders/src/main.fs +++ b/shaders/src/main.fs @@ -77,7 +77,7 @@ void main() { vec4 c = vec4(1.0, 0, 1.0, 1.0) * a; fragColor = mix(fragColor, c, 0.2); } else { - highp vec2 size = vec2(textureSize(light_shadowMap, 0)); + highp vec2 size = vec2(textureSize(sampler0_shadowMap, 0)); highp int ix = int(floor(p.x * size.x)); highp int iy = int(floor(p.y * size.y)); float t = float((ix ^ iy) & 1) * 0.2; diff --git a/shaders/src/shading_unlit.fs b/shaders/src/shading_unlit.fs index 33ea12cd80a..a3ff0059c86 100644 --- a/shaders/src/shading_unlit.fs +++ b/shaders/src/shading_unlit.fs @@ -46,7 +46,7 @@ vec4 evaluateMaterial(const MaterialInputs material) { bool hasDirectionalShadows = bool(frameUniforms.directionalShadows & 1); if (hasDirectionalShadows && cascadeHasVisibleShadows) { highp vec4 shadowPosition = getShadowPosition(cascade); - visibility = shadow(true, light_shadowMap, cascade, shadowPosition, 0.0); + visibility = shadow(true, sampler0_shadowMap, cascade, shadowPosition, 0.0); // shadow far attenuation highp vec3 v = getWorldPosition() - getWorldCameraPosition(); // (viewFromWorld * v).z == dot(transpose(viewFromWorld), v) diff --git a/shaders/src/shadowing.fs b/shaders/src/shadowing.fs index 9a3eb63302f..50f44e26c1d 100644 --- a/shaders/src/shadowing.fs +++ b/shaders/src/shadowing.fs @@ -400,7 +400,7 @@ float screenSpaceContactShadow(vec3 lightDirection) { highp vec3 ray; for (int i = 0 ; i < kStepCount ; i++, t += dt) { ray = rayData.uvRayStart + rayData.uvRay * t; - highp float z = textureLod(light_structure, uvToRenderTargetUV(ray.xy), 0.0).r; + highp float z = textureLod(sampler0_structure, uvToRenderTargetUV(ray.xy), 0.0).r; highp float dz = z - ray.z; if (abs(tolerance - dz) < tolerance) { occlusion = 1.0; From 29ecfd5fdab1bdf5d4d744487275b079fcda63a4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 7 Jun 2024 11:18:40 -0700 Subject: [PATCH 02/49] generate the proper per-view descriptor-set layout in the shader the per-view descriptor-set layout depends on both the material and the variant. --- .../include/private/filament/DescriptorSets.h | 1 - libs/filabridge/src/DescriptorSets.cpp | 15 +++++----- libs/filamat/src/shaders/ShaderGenerator.cpp | 28 +++++++++++++++---- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/libs/filabridge/include/private/filament/DescriptorSets.h b/libs/filabridge/include/private/filament/DescriptorSets.h index 287702f7d0e..171c8650d10 100644 --- a/libs/filabridge/include/private/filament/DescriptorSets.h +++ b/libs/filabridge/include/private/filament/DescriptorSets.h @@ -32,7 +32,6 @@ backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept; backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept; backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept; -backend::DescriptorSetLayout const& getPerViewLayout() noexcept; // @deprecated backend::DescriptorSetLayout getPerViewDescriptorSetLayout( MaterialDomain domain, UserVariantFilterMask variantFilter, diff --git a/libs/filabridge/src/DescriptorSets.cpp b/libs/filabridge/src/DescriptorSets.cpp index f0a138bc5cd..a9909fb01ba 100644 --- a/libs/filabridge/src/DescriptorSets.cpp +++ b/libs/filabridge/src/DescriptorSets.cpp @@ -102,10 +102,6 @@ DescriptorSetLayout const& getSsrVariantLayout() noexcept { return ssrVariantDescriptorSetLayout; } -DescriptorSetLayout const& getPerViewLayout() noexcept { - return perViewDescriptorSetLayout; -} - DescriptorSetLayout const& getPerRenderableLayout() noexcept { return perRenderableDescriptorSetLayout; } @@ -160,6 +156,10 @@ DescriptorSetLayout getPerViewDescriptorSetLayout( bool isLit, ReflectionMode reflectionMode, RefractionMode refractionMode) noexcept { + + bool const ssr = reflectionMode == ReflectionMode::SCREEN_SPACE || + refractionMode == RefractionMode::SCREEN_SPACE; + switch (domain) { case MaterialDomain::SURFACE: { // @@ -171,14 +171,13 @@ DescriptorSetLayout getPerViewDescriptorSetLayout( layout.bindings.erase( std::remove_if(layout.bindings.begin(), layout.bindings.end(), [](auto const& entry) { - return entry.binding == PerViewBindingPoints::IBL_DFG_LUT || - entry.binding == PerViewBindingPoints::IBL_SPECULAR; + return entry.binding == PerViewBindingPoints::IBL_DFG_LUT || + entry.binding == PerViewBindingPoints::IBL_SPECULAR; }), layout.bindings.end()); } // remove descriptors not needed for SSRs - if (reflectionMode != ReflectionMode::SCREEN_SPACE && - refractionMode != RefractionMode::SCREEN_SPACE) { + if (!ssr) { layout.bindings.erase( std::remove_if(layout.bindings.begin(), layout.bindings.end(), [](auto const& entry) { diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index ccedf738bd6..93e6485b236 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -604,12 +604,30 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel, CodeGenerator::generateSeparator(fs); if (featureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + assert_invariant(mMaterialDomain == MaterialDomain::SURFACE); - // We need to filter out all the descriptors not included in the "resolved" layout below - backend::DescriptorSetLayout const perViewDescriptorSetLayout = - descriptor_sets::getPerViewDescriptorSetLayout( - mMaterialDomain, variantFilter, - material.isLit, material.reflectionMode, material.refractionMode); + auto getPerViewDescriptorSetLayoutWithVariant = []( + filament::Variant variant, + UserVariantFilterMask variantFilter, + bool isLit, + ReflectionMode reflectionMode, + RefractionMode refractionMode) -> backend::DescriptorSetLayout { + + if (filament::Variant::isValidDepthVariant(variant)) { + return descriptor_sets::getDepthVariantLayout(); + } + if (filament::Variant::isSSRVariant(variant)) { + return descriptor_sets::getSsrVariantLayout(); + } + // We need to filter out all the descriptors not included in the "resolved" layout below + return descriptor_sets::getPerViewDescriptorSetLayout( + MaterialDomain::SURFACE, variantFilter, + isLit, reflectionMode, refractionMode); + }; + + auto const perViewDescriptorSetLayout = getPerViewDescriptorSetLayoutWithVariant( + variant, variantFilter, + material.isLit, material.reflectionMode, material.refractionMode); // this is the list of samplers we need to filter auto list = SibGenerator::getPerViewSib(variant).getSamplerInfoList(); From 38a3c52629f2c6dbd23b16a58e090e8ad1cfe39b Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 23 May 2024 17:39:20 -0700 Subject: [PATCH 03/49] vk: build new descriptor set framework --- .../backend/include/backend/DriverEnums.h | 1 + .../backend/src/opengl/GLDescriptorSet.cpp | 2 + filament/backend/src/opengl/OpenGLProgram.cpp | 2 + filament/backend/src/vulkan/VulkanBlitter.cpp | 31 +- .../backend/src/vulkan/VulkanCommands.cpp | 23 +- filament/backend/src/vulkan/VulkanCommands.h | 32 + filament/backend/src/vulkan/VulkanDriver.cpp | 278 ++--- filament/backend/src/vulkan/VulkanDriver.h | 3 +- filament/backend/src/vulkan/VulkanHandles.cpp | 254 ++-- filament/backend/src/vulkan/VulkanHandles.h | 68 +- .../backend/src/vulkan/VulkanImageUtility.cpp | 9 +- .../backend/src/vulkan/VulkanImageUtility.h | 4 +- .../src/vulkan/VulkanPipelineCache.cpp | 6 +- .../backend/src/vulkan/VulkanReadPixels.cpp | 4 +- .../backend/src/vulkan/VulkanSwapChain.cpp | 4 +- filament/backend/src/vulkan/VulkanTexture.cpp | 100 +- filament/backend/src/vulkan/VulkanTexture.h | 15 +- filament/backend/src/vulkan/VulkanUtility.h | 56 +- .../caching/VulkanDescriptorSetManager.cpp | 1112 ++++------------- .../caching/VulkanDescriptorSetManager.h | 52 +- .../caching/VulkanPipelineLayoutCache.cpp | 9 +- .../caching/VulkanPipelineLayoutCache.h | 14 +- 22 files changed, 663 insertions(+), 1416 deletions(-) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index b01adc7ad09..c15cbedcbb1 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -226,6 +226,7 @@ enum class DescriptorType : uint8_t { UNIFORM_BUFFER, SHADER_STORAGE_BUFFER, SAMPLER, + INPUT_ATTACHMENT, }; enum class DescriptorFlags : uint8_t { diff --git a/filament/backend/src/opengl/GLDescriptorSet.cpp b/filament/backend/src/opengl/GLDescriptorSet.cpp index 8669236ca27..b8070b64b15 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.cpp +++ b/filament/backend/src/opengl/GLDescriptorSet.cpp @@ -105,6 +105,8 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds } } break; + case DescriptorType::INPUT_ATTACHMENT: + break; } } } diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 42943075ee0..b3935ed1f0c 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -182,6 +182,8 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra } break; } + case DescriptorType::INPUT_ATTACHMENT: + break; } CHECK_GL_ERROR(utils::slog.e) } diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp index 0decae3769e..7100b026352 100644 --- a/filament/backend/src/vulkan/VulkanBlitter.cpp +++ b/filament/backend/src/vulkan/VulkanBlitter.cpp @@ -15,6 +15,7 @@ */ #include "VulkanBlitter.h" +#include "VulkanCommands.h" #include "VulkanContext.h" #include "VulkanFboCache.h" #include "VulkanHandles.h" @@ -33,9 +34,10 @@ namespace filament::backend { namespace { -inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VkFilter filter, +inline void blitFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect, VkFilter filter, VulkanAttachment src, VulkanAttachment dst, const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) { + VkCommandBuffer const cmdbuf = commands->buffer(); if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) { FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level << " layout=" << src.getLayout() @@ -49,8 +51,8 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VulkanLayout oldSrcLayout = src.getLayout(); VulkanLayout oldDstLayout = dst.getLayout(); - src.texture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC); - dst.texture->transitionLayout(cmdbuffer, dstRange, VulkanLayout::TRANSFER_DST); + src.texture->transitionLayout(commands, srcRange, VulkanLayout::TRANSFER_SRC); + dst.texture->transitionLayout(commands, dstRange, VulkanLayout::TRANSFER_DST); const VkImageBlit blitRegions[1] = {{ .srcSubresource = { aspect, src.level, src.layer, 1 }, @@ -58,7 +60,7 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, .dstSubresource = { aspect, dst.level, dst.layer, 1 }, .dstOffsets = { dstRect[0], dstRect[1] }, }}; - vkCmdBlitImage(cmdbuffer, + vkCmdBlitImage(cmdbuf, src.getImage(), imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC), dst.getImage(), imgutil::getVkLayout(VulkanLayout::TRANSFER_DST), 1, blitRegions, filter); @@ -69,12 +71,13 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, if (oldDstLayout == VulkanLayout::UNDEFINED) { oldDstLayout = imgutil::getDefaultLayout(dst.texture->usage); } - src.texture->transitionLayout(cmdbuffer, srcRange, oldSrcLayout); - dst.texture->transitionLayout(cmdbuffer, dstRange, oldDstLayout); + src.texture->transitionLayout(commands, srcRange, oldSrcLayout); + dst.texture->transitionLayout(commands, dstRange, oldDstLayout); } -inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, +inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect, VulkanAttachment src, VulkanAttachment dst) { + VkCommandBuffer const cmdbuffer = commands->buffer(); if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) { FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level << " layout=" << src.getLayout() @@ -88,8 +91,8 @@ inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspe VulkanLayout oldSrcLayout = src.getLayout(); VulkanLayout oldDstLayout = dst.getLayout(); - src.texture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC); - dst.texture->transitionLayout(cmdbuffer, dstRange, VulkanLayout::TRANSFER_DST); + src.texture->transitionLayout(commands, srcRange, VulkanLayout::TRANSFER_SRC); + dst.texture->transitionLayout(commands, dstRange, VulkanLayout::TRANSFER_DST); assert_invariant( aspect != VK_IMAGE_ASPECT_DEPTH_BIT && "Resolve with depth is not yet supported."); @@ -111,8 +114,8 @@ inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspe if (oldDstLayout == VulkanLayout::UNDEFINED) { oldDstLayout = imgutil::getDefaultLayout(dst.texture->usage); } - src.texture->transitionLayout(cmdbuffer, srcRange, oldSrcLayout); - dst.texture->transitionLayout(cmdbuffer, dstRange, oldDstLayout); + src.texture->transitionLayout(commands, srcRange, oldSrcLayout); + dst.texture->transitionLayout(commands, dstRange, oldDstLayout); } struct BlitterUniforms { @@ -149,10 +152,9 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) { #endif VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuffer = commands.buffer(); commands.acquire(src.texture); commands.acquire(dst.texture); - resolveFast(cmdbuffer, aspect, src, dst); + resolveFast(&commands, aspect, src, dst); } void VulkanBlitter::blit(VkFilter filter, @@ -175,10 +177,9 @@ void VulkanBlitter::blit(VkFilter filter, // src and dst should have the same aspect here VkImageAspectFlags const aspect = src.texture->getImageAspect(); VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuffer = commands.buffer(); commands.acquire(src.texture); commands.acquire(dst.texture); - blitFast(cmdbuffer, aspect, filter, src, dst, srcRectPair, dstRectPair); + blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair); } void VulkanBlitter::terminate() noexcept { diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp index 473ca8f1c16..ee8ffe24971 100644 --- a/filament/backend/src/vulkan/VulkanCommands.cpp +++ b/filament/backend/src/vulkan/VulkanCommands.cpp @@ -297,11 +297,11 @@ bool VulkanCommands::flush() { #endif auto& cmdfence = currentbuf->fence; - std::unique_lock lock(cmdfence->mutex); - cmdfence->status.store(VK_NOT_READY); - UTILS_UNUSED_IN_RELEASE VkResult result = vkQueueSubmit(mQueue, 1, &submitInfo, cmdfence->fence); - cmdfence->condition.notify_all(); - lock.unlock(); + UTILS_UNUSED_IN_RELEASE VkResult result = VK_SUCCESS; + { + auto scope = cmdfence->setValue(VK_NOT_READY); + result = vkQueueSubmit(mQueue, 1, &submitInfo, cmdfence->getFence()); + } #if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER) if (result != VK_SUCCESS) { @@ -340,7 +340,7 @@ void VulkanCommands::wait() { auto wrapper = mStorage[i].get(); if (wrapper->buffer() != VK_NULL_HANDLE && mCurrentCommandBufferIndex != static_cast(i)) { - fences[count++] = wrapper->fence->fence; + fences[count++] = wrapper->fence->getFence(); } } if (count > 0) { @@ -361,12 +361,13 @@ void VulkanCommands::gc() { if (wrapper->buffer() == VK_NULL_HANDLE) { continue; } - VkResult const result = vkGetFenceStatus(mDevice, wrapper->fence->fence); + auto const vkfence = wrapper->fence->getFence(); + VkResult const result = vkGetFenceStatus(mDevice, vkfence); if (result != VK_SUCCESS) { continue; } - fences[count++] = wrapper->fence->fence; - wrapper->fence->status.store(VK_SUCCESS); + fences[count++] = vkfence; + wrapper->fence->setValue(VK_SUCCESS); wrapper->reset(); mAvailableBufferCount++; } @@ -383,9 +384,9 @@ void VulkanCommands::updateFences() { if (wrapper->buffer() != VK_NULL_HANDLE) { VulkanCmdFence* fence = wrapper->fence.get(); if (fence) { - VkResult status = vkGetFenceStatus(mDevice, fence->fence); + VkResult status = vkGetFenceStatus(mDevice, fence->getFence()); // This is either VK_SUCCESS, VK_NOT_READY, or VK_ERROR_DEVICE_LOST. - fence->status.store(status, std::memory_order_relaxed); + fence->setValue(status); } } } diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index e3c7a92f190..a946fc4314a 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -61,8 +61,40 @@ class VulkanGroupMarkers { // Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences. struct VulkanCmdFence { + struct SetValueScope { + public: + ~SetValueScope() { + mHolder->mutex.unlock(); + mHolder->condition.notify_all(); + } + + private: + SetValueScope(VulkanCmdFence* fenceHolder, VkResult result) : + mHolder(fenceHolder) { + mHolder->mutex.lock(); + mHolder->status.store(result); + } + VulkanCmdFence* mHolder; + friend struct VulkanCmdFence; + }; + VulkanCmdFence(VkFence ifence); ~VulkanCmdFence() = default; + + SetValueScope setValue(VkResult value) { + return {this, value}; + } + + VkFence& getFence() { + return fence; + } + + VkResult getStatus() { + std::unique_lock lock(mutex); + return status.load(std::memory_order_acquire); + } + +private: VkFence fence; utils::Condition condition; utils::Mutex mutex; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 0057b89d314..83413208a15 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -149,6 +149,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla } #endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS) + }// anonymous namespace #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) @@ -261,10 +262,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mDescriptorSetManager.setPlaceHolders(mSamplerCache.getSampler({}), mEmptyTexture, mEmptyBufferObject); - - mGetPipelineFunction = [this](VulkanDescriptorSetLayoutList const& layouts, VulkanProgram* program) { - return mPipelineLayoutCache.getLayout(layouts, program); - }; } VulkanDriver::~VulkanDriver() noexcept = default; @@ -387,7 +384,6 @@ void VulkanDriver::collectGarbage() { mStagePool.gc(); mFramebufferCache.gc(); mPipelineCache.gc(); - mDescriptorSetManager.gc(); #if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK) mResourceAllocator.print(); @@ -428,6 +424,9 @@ void VulkanDriver::updateDescriptorSetBuffer( backend::BufferObjectHandle boh, uint32_t offset, uint32_t size) { + VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + VulkanBufferObject* obj = mResourceAllocator.handle_cast(boh); + mDescriptorSetManager.updateBuffer(set, binding, obj, offset, size); } void VulkanDriver::updateDescriptorSetTexture( @@ -435,6 +434,18 @@ void VulkanDriver::updateDescriptorSetTexture( backend::descriptor_binding_t binding, backend::TextureHandle th, SamplerParams params) { + VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + VulkanTexture* texture = mResourceAllocator.handle_cast(th); + + // We need to make sure the initial layout transition has been completed before we can write + // the sampler descriptor. We flush and wait until the transition has been completed. + if (!texture->transitionReady()) { + mCommands.flush(); + mCommands.wait(); + } + + VkSampler const vksampler = mSamplerCache.getSampler(params); + mDescriptorSetManager.updateSampler(set, binding, texture, vksampler); } void VulkanDriver::flush(int) { @@ -457,8 +468,7 @@ void VulkanDriver::finish(int dummy) { void VulkanDriver::createSamplerGroupR(Handle sbh, uint32_t count, utils::FixedSizeString<32> debugName) { - auto sg = mResourceAllocator.construct(sbh, count); - mResourceManager.acquire(sg); + PANIC_PRECONDITION("Sampler Group is deprecated"); } void VulkanDriver::createRenderPrimitiveR(Handle rph, @@ -586,7 +596,6 @@ void VulkanDriver::destroyProgram(Handle ph) { return; } auto vkprogram = mResourceAllocator.handle_cast(ph); - mDescriptorSetManager.clearProgram(vkprogram); mResourceManager.release(vkprogram); } @@ -703,10 +712,18 @@ void VulkanDriver::createTimerQueryR(Handle tqh, int) { void VulkanDriver::createDescriptorSetLayoutR(Handle dslh, backend::DescriptorSetLayout&& info) { + VulkanDescriptorSetLayout* layout = mResourceAllocator.construct( + dslh, mPlatform->getDevice(), info); + mResourceManager.acquire(layout); } void VulkanDriver::createDescriptorSetR(Handle dsh, Handle dslh) { + auto layout = mResourceAllocator.handle_cast(dslh); + mDescriptorSetManager.createSet(dsh, layout); + + auto set = mResourceAllocator.handle_cast(dsh); + mResourceManager.acquire(set); } Handle VulkanDriver::createVertexBufferInfoS() noexcept { @@ -780,11 +797,11 @@ Handle VulkanDriver::createTimerQueryS() noexcept { } Handle VulkanDriver::createDescriptorSetLayoutS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceAllocator.allocHandle(); } Handle VulkanDriver::createDescriptorSetS() noexcept { - return mResourceAllocator.allocHandle(); + return mResourceAllocator.allocHandle(); } void VulkanDriver::destroySamplerGroup(Handle sbh) { @@ -827,9 +844,14 @@ void VulkanDriver::destroyTimerQuery(Handle tqh) { } void VulkanDriver::destroyDescriptorSetLayout(Handle dslh) { + VulkanDescriptorSetLayout* layout = mResourceAllocator.handle_cast(dslh); + mResourceManager.release(layout); } void VulkanDriver::destroyDescriptorSet(Handle dsh) { + mDescriptorSetManager.destroySet(dsh); + VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + mResourceManager.release(set); } Handle VulkanDriver::createStreamNative(void* nativeStream) { @@ -869,8 +891,7 @@ FenceStatus VulkanDriver::getFenceStatus(Handle fh) { // Internally we use the VK_INCOMPLETE status to mean "not yet submitted". // When this fence gets submitted, its status changes to VK_NOT_READY. - std::unique_lock lock(cmdfence->mutex); - if (cmdfence->status.load() == VK_SUCCESS) { + if (cmdfence->getStatus() == VK_SUCCESS) { return FenceStatus::CONDITION_SATISFIED; } @@ -921,7 +942,9 @@ bool VulkanDriver::isRenderTargetFormatSupported(TextureFormat format) { } bool VulkanDriver::isFrameBufferFetchSupported() { - return true; + // TODO: we must fix this before landing descriptor set change. Otherwise, the scuba tests will fail. + //return true; + return false; } bool VulkanDriver::isFrameBufferFetchMultiSampleSupported() { @@ -1106,7 +1129,8 @@ void VulkanDriver::resetBufferObject(Handle boh) { } void VulkanDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { - mResourceAllocator.handle_cast(th)->setPrimaryRange(minLevel, maxLevel); + auto texture = mResourceAllocator.handle_cast(th); + texture->setPrimaryRange(minLevel, maxLevel); } void VulkanDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, @@ -1199,18 +1223,7 @@ void VulkanDriver::generateMipmaps(Handle th) { void VulkanDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { - auto* sb = mResourceAllocator.handle_cast(sbh); - - // FIXME: we shouldn't be using SamplerGroup here, instead the backend should create - // a descriptor or any internal data-structure that represents the textures/samplers. - // It's preferable to do as much work as possible here. - // Here, we emulate the older backend API by re-creating a SamplerGroup from the - // passed data. - SamplerGroup samplerGroup(data.size / sizeof(SamplerDescriptor)); - memcpy(samplerGroup.data(), data.buffer, data.size); - *sb->sb = std::move(samplerGroup); - - scheduleDestroy(std::move(data)); + PANIC_PRECONDITION("Sampler Group is deprecated"); } void VulkanDriver::compilePrograms(CompilerPriorityQueue priority, @@ -1225,7 +1238,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP FVK_SYSTRACE_START("beginRenderPass"); VulkanRenderTarget* const rt = mResourceAllocator.handle_cast(rth); - const VkExtent2D extent = rt->getExtent(); + VkExtent2D const extent = rt->getExtent(); assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0); // Filament has the expectation that the contents of the swap chain are not preserved on the @@ -1255,36 +1268,6 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // the non-sampling case. VulkanCommandBuffer& commands = mCommands.get(); VkCommandBuffer const cmdbuffer = commands.buffer(); - - UTILS_NOUNROLL - for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; - samplerGroupIdx++) { - VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupIdx]; - if (!vksb) { - continue; - } - SamplerGroup* sb = vksb->sb.get(); - for (size_t i = 0; i < sb->getSize(); i++) { - SamplerDescriptor const* boundSampler = sb->data() + i; - if (UTILS_LIKELY(boundSampler->t)) { - VulkanTexture* texture - = mResourceAllocator.handle_cast(boundSampler->t); - if (!any(texture->usage & TextureUsage::DEPTH_ATTACHMENT)) { - continue; - } - if (texture->getPrimaryImageLayout() == VulkanLayout::DEPTH_SAMPLER) { - continue; - } - commands.acquire(texture); - - // Transition the primary view, which is the sampler's view into the right layout. - texture->transitionLayout(cmdbuffer, texture->getPrimaryViewRange(), - VulkanLayout::DEPTH_SAMPLER); - break; - } - } - } - VulkanLayout currentDepthLayout = depth.getLayout(); TargetBufferFlags clearVal = params.flags.clear; @@ -1297,11 +1280,9 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP // If the depth attachment texture was previously sampled, then we need to manually // transition it to an attachment. This is necessary to also set up a barrier between the // previous read and the potentially coming write. - if (currentDepthLayout == VulkanLayout::DEPTH_SAMPLER) { - depth.texture->transitionLayout(cmdbuffer, depth.getSubresourceRange(), - VulkanLayout::DEPTH_ATTACHMENT); - currentDepthLayout = VulkanLayout::DEPTH_ATTACHMENT; - } + depth.texture->transitionLayout(&commands, depth.getSubresourceRange(), + VulkanLayout::DEPTH_ATTACHMENT); + currentDepthLayout = VulkanLayout::DEPTH_ATTACHMENT; } // Create the VkRenderPass or fetch it from cache. @@ -1316,18 +1297,13 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP .subpassMask = uint8_t(params.subpassMask), }; for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { - const VulkanAttachment& info = rt->getColor(i); + VulkanAttachment const& info = rt->getColor(i); if (info.texture) { rpkey.initialColorLayoutMask |= 1 << i; rpkey.colorFormat[i] = info.getFormat(); if (rpkey.samples > 1 && info.texture->samples == 1) { rpkey.needsResolveMask |= (1 << i); } - if (info.texture->getPrimaryImageLayout() != VulkanLayout::COLOR_ATTACHMENT) { - ((VulkanTexture*) info.texture) - ->transitionLayout(cmdbuffer, info.getSubresourceRange(), - VulkanLayout::COLOR_ATTACHMENT); - } } else { rpkey.colorFormat[i] = VK_FORMAT_UNDEFINED; } @@ -1346,31 +1322,34 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP }; auto& renderPassAttachments = mRenderPassFboInfo.attachments; for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) { - if (!rt->getColor(i).texture) { + VulkanAttachment& attachment = rt->getColor(i); + if (!attachment.texture) { fbkey.color[i] = VK_NULL_HANDLE; fbkey.resolve[i] = VK_NULL_HANDLE; - } else if (fbkey.samples == 1) { - auto& colorAttachment = rt->getColor(i); - renderPassAttachments.insert(colorAttachment); - fbkey.color[i] = colorAttachment.getImageView(); + continue; + } + auto const& range = attachment.getSubresourceRange(); + attachment.texture->transitionLayout(&commands, range, VulkanLayout::COLOR_ATTACHMENT); + + renderPassAttachments.insert(attachment); + if (fbkey.samples == 1) { + fbkey.color[i] = attachment.getImageView(); fbkey.resolve[i] = VK_NULL_HANDLE; assert_invariant(fbkey.color[i]); } else { auto& msaaColorAttachment = rt->getMsaaColor(i); + auto const& msaaRange = attachment.getSubresourceRange(); renderPassAttachments.insert(msaaColorAttachment); - - auto& colorAttachment = rt->getColor(i); + msaaColorAttachment.texture->transitionLayout(&commands, msaaRange, + VulkanLayout::COLOR_ATTACHMENT); fbkey.color[i] = msaaColorAttachment.getImageView(); + assert_invariant(fbkey.color[i]); - VulkanTexture* texture = colorAttachment.texture; - if (texture->samples == 1) { - mRenderPassFboInfo.hasColorResolve = true; + assert_invariant(attachment.texture->samples == 1); - renderPassAttachments.insert(colorAttachment); - fbkey.resolve[i] = colorAttachment.getImageView(); - assert_invariant(fbkey.resolve[i]); - } - assert_invariant(fbkey.color[i]); + mRenderPassFboInfo.hasColorResolve = true; + fbkey.resolve[i] = attachment.getImageView(); + assert_invariant(fbkey.resolve[i]); } } if (depth.texture) { @@ -1491,51 +1470,20 @@ void VulkanDriver::endRenderPass(int) { // require more state tracking, so we've chosen to use a memory barrier for simplicity and // correctness. if (!rt->isSwapChain()) { - for (auto const& attachment: mRenderPassFboInfo.attachments) { + for (auto& attachment: mRenderPassFboInfo.attachments) { + auto const& range = attachment.getSubresourceRange(); bool const isDepth = attachment.isDepth(); - VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - - // This is a workaround around a validation issue (might not be an actual driver issue). - if (mRenderPassFboInfo.hasColorResolve && !isDepth) { - srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - } - - VkPipelineStageFlags dstStageMask = - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - VkAccessFlags srcAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - VkAccessFlags dstAccess = VK_ACCESS_SHADER_READ_BIT; - VulkanLayout layout = VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT; if (isDepth) { - srcAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - dstAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; - srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - layout = VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT; + attachment.texture->setLayout(range, VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT); + attachment.texture->transitionLayout(&commands, range, VulkanLayout::DEPTH_SAMPLER); + } else { + attachment.texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT); + attachment.texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE); } - - auto const vkLayout = imgutil::getVkLayout(layout); - auto const& range = attachment.getSubresourceRange(); - VkImageMemoryBarrier barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = srcAccess, - .dstAccessMask = dstAccess, - .oldLayout = vkLayout, - .newLayout = vkLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = attachment.getImage(), - .subresourceRange = range, - }; - - attachment.texture->setLayout(range, layout); - vkCmdPipelineBarrier(cmdbuffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, - 1, &barrier); } } mRenderPassFboInfo.clear(); - mDescriptorSetManager.clearState(); mCurrentRenderPass.renderTarget = nullptr; mCurrentRenderPass.renderPass = VK_NULL_HANDLE; FVK_SYSTRACE_END(); @@ -1595,21 +1543,10 @@ void VulkanDriver::commit(Handle sch) { } void VulkanDriver::bindUniformBuffer(uint32_t index, Handle boh) { - auto* bo = mResourceAllocator.handle_cast(boh); - VkDeviceSize const offset = 0; - VkDeviceSize const size = VK_WHOLE_SIZE; - mDescriptorSetManager.updateBuffer({}, (uint32_t) index, bo, offset, size); } void VulkanDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index, Handle boh, uint32_t offset, uint32_t size) { - - assert_invariant(bindingType == BufferObjectBinding::UNIFORM); - - // TODO: implement BufferObjectBinding::SHADER_STORAGE case - - auto* bo = mResourceAllocator.handle_cast(boh); - mDescriptorSetManager.updateBuffer({}, (uint32_t) index, bo, offset, size); } void VulkanDriver::bindSamplers(uint32_t index, Handle sbh) { @@ -1817,8 +1754,8 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { *mResourceAllocator.handle_cast(pipelineState.vertexBufferInfo); Handle programHandle = pipelineState.program; - RasterState rasterState = pipelineState.rasterState; - PolygonOffset depthOffset = pipelineState.polygonOffset; + RasterState const& rasterState = pipelineState.rasterState; + PolygonOffset const& depthOffset = pipelineState.polygonOffset; auto* program = mResourceAllocator.handle_cast(programHandle); commands->acquire(program); @@ -1861,59 +1798,26 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { mPipelineCache.bindPrimitiveTopology(topology); mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount()); - // Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding), - // where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract - // Filament concept used to form groups of samplers. - - auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex(); -#if FVK_ENABLED_DEBUG_SAMPLER_NAME - auto const& bindingToName = program->getBindingToName(); -#endif - - for (auto binding: program->getBindings()) { - uint16_t const indexPair = bindingToSamplerIndex[binding]; - if (indexPair == 0xffff) { - continue; - } - - uint16_t const samplerGroupInd = (indexPair >> 8) & 0xff; - uint16_t const samplerInd = (indexPair & 0xff); - - VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupInd]; - if (!vksb) { - continue; - } - SamplerDescriptor const* boundSampler = ((SamplerDescriptor*) vksb->sb->data()) + samplerInd; - - if (UTILS_UNLIKELY(!boundSampler->t)) { - continue; - } - VulkanTexture* texture = mResourceAllocator.handle_cast(boundSampler->t); - - // TODO: can this uninitialized check be checked in a higher layer? - // This fallback path is very flaky because the dummy texture might not have - // matching characteristics. (e.g. if the missing texture is a 3D texture) - if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) { -#if FVK_ENABLED(FVK_DEBUG_TEXTURE) && FVK_ENABLED_DEBUG_SAMPLER_NAME - FVK_LOGW << "Uninitialized texture bound to '" << bindingToName[binding] << "'"; - FVK_LOGW << " in material '" << program->name.c_str() << "'"; - FVK_LOGW << " at binding point " << +binding << utils::io::endl; -#endif - texture = mEmptyTexture; - } + auto& setLayouts = pipelineState.pipelineLayout.setLayout; + VulkanDescriptorSetLayout::DescriptorSetLayoutArray layoutList; + uint8_t layoutCount = 0; + std::transform(setLayouts.begin(), setLayouts.end(), layoutList.begin(), + [&](Handle handle) -> VkDescriptorSetLayout { + if (!handle) { + return VK_NULL_HANDLE; + } + auto layout = mResourceAllocator.handle_cast(handle); + layoutCount++; + return layout->vklayout; + }); + auto pipelineLayout = mPipelineLayoutCache.getLayout(layoutList, program); - VkSampler const vksampler = mSamplerCache.getSampler(boundSampler->s); -#if FVK_ENABLED_DEBUG_SAMPLER_NAME - VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER, - reinterpret_cast(vksampler), bindingToName[binding].c_str()); -#endif - mDescriptorSetManager.updateSampler({}, binding, texture, vksampler); - } + constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF}; - auto const pipelineLayout = mDescriptorSetManager.bind(commands, program, mGetPipelineFunction); mBoundPipeline = { .program = program, .pipelineLayout = pipelineLayout, + .descriptorSetMask = descriptorSetMaskTable[layoutCount], }; mPipelineCache.bindLayout(pipelineLayout); @@ -1958,8 +1862,10 @@ void VulkanDriver::bindRenderPrimitive(Handle rph) { void VulkanDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, - backend::descriptor_set_t set, + backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) { + VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + mDescriptorSetManager.bind(setIndex, set, std::move(offsets)); } void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { @@ -1969,8 +1875,8 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins VulkanCommandBuffer& commands = mCommands.get(); VkCommandBuffer cmdbuffer = commands.buffer(); - // Bind "dynamic" UBOs if they need to change. - mDescriptorSetManager.dynamicBind(&commands, {}); + mDescriptorSetManager.commit(&commands, mBoundPipeline.pipelineLayout, + mBoundPipeline.descriptorSetMask); // Finally, make the actual draw call. TODO: support subranges const uint32_t firstIndex = indexOffset; @@ -2018,7 +1924,7 @@ void VulkanDriver::scissor(Viewport scissorBox) { .extent = { uint32_t(r - l), uint32_t(t - b) } }; - const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget; + VulkanRenderTarget const* rt = mCurrentRenderPass.renderTarget; rt->transformClientRectToPlatform(&scissor); vkCmdSetScissor(cmdbuffer, 0, 1, &scissor); } diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index abdbc6304f6..84609f41d89 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -159,12 +159,11 @@ class VulkanDriver final : public DriverBase { VulkanReadPixels mReadPixels; VulkanDescriptorSetManager mDescriptorSetManager; - VulkanDescriptorSetManager::GetPipelineLayoutFunction mGetPipelineFunction; - // This is necessary for us to write to push constants after binding a pipeline. struct BoundPipeline { VulkanProgram* program; VkPipelineLayout pipelineLayout; + uint8_t descriptorSetMask; }; BoundPipeline mBoundPipeline = {}; RenderPassFboBundle mRenderPassFboInfo; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 3cd2ff38fc6..7a2041f9fdd 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -16,13 +16,11 @@ #include "VulkanHandles.h" -#include "VulkanDriver.h" #include "VulkanConstants.h" -#include "VulkanDriver.h" #include "VulkanMemory.h" +#include "VulkanResourceAllocator.h" #include "VulkanUtility.h" #include "spirv/VulkanSpirvUtils.h" -#include "utils/Log.h" #include @@ -54,57 +52,17 @@ void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeight) { } template -static constexpr Bitmask fromStageFlags(ShaderStageFlags2 flags, uint8_t binding) { +static constexpr Bitmask fromStageFlags(ShaderStageFlags flags, uint8_t binding) { Bitmask ret = 0; - if (flags & ShaderStageFlags2::VERTEX) { + if ((flags & ShaderStageFlags::VERTEX) != ShaderStageFlags::NONE) { ret |= (getVertexStage() << binding); } - if (flags & ShaderStageFlags2::FRAGMENT) { + if ((flags & ShaderStageFlags::FRAGMENT) != ShaderStageFlags::NONE) { ret |= (getFragmentStage() << binding); } return ret; } -constexpr decltype(VulkanProgram::MAX_SHADER_MODULES) MAX_SHADER_MODULES = - VulkanProgram::MAX_SHADER_MODULES; - -using LayoutDescriptionList = VulkanProgram::LayoutDescriptionList; - -template -void addDescriptors(Bitmask mask, - utils::FixedCapacityVector& outputList) { - constexpr uint8_t MODULE_OFFSET = (sizeof(Bitmask) * 8) / MAX_SHADER_MODULES; - for (uint8_t i = 0; i < MODULE_OFFSET; ++i) { - bool const hasVertex = (mask & (1ULL << i)) != 0; - bool const hasFragment = (mask & (1ULL << (MODULE_OFFSET + i))) != 0; - if (!hasVertex && !hasFragment) { - continue; - } - - descset::DescriptorSetLayoutBinding binding{ - .binding = i, - .flags = descset::DescriptorFlags::NONE, - .count = 0,// This is always 0 for now as we pass the size of the UBOs in the Driver API - // instead. - }; - if (hasVertex) { - binding.stageFlags = ShaderStageFlags2::VERTEX; - } - if (hasFragment) { - binding.stageFlags = static_cast( - binding.stageFlags | ShaderStageFlags2::FRAGMENT); - } - if constexpr (std::is_same_v) { - binding.type = descset::DescriptorType::UNIFORM_BUFFER; - } else if constexpr (std::is_same_v) { - binding.type = descset::DescriptorType::SAMPLER; - } else if constexpr (std::is_same_v) { - binding.type = descset::DescriptorType::INPUT_ATTACHMENT; - } - outputList.push_back(binding); - } -} - inline VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo const& info) { VkDescriptorSetLayout layout; @@ -123,22 +81,116 @@ inline VkShaderStageFlags getVkStage(backend::ShaderStage stage) { } } -} // anonymous namespace +inline VkDescriptorSetLayoutCreateInfo getLayoutCreateInfo(DescriptorSetLayout const& layout) { + // Note that the following *needs* to be static so that VkDescriptorSetLayoutCreateInfo will not + // refer to stack memory. + static VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS]; + uint32_t count = 0; + for (auto const& binding: layout.bindings) { + VkShaderStageFlags stages = 0; + VkDescriptorType type; + + if ((binding.stageFlags & ShaderStageFlags::VERTEX) != ShaderStageFlags::NONE) { + stages |= VK_SHADER_STAGE_VERTEX_BIT; + } + if ((binding.stageFlags & ShaderStageFlags::FRAGMENT) != ShaderStageFlags::NONE) { + stages |= VK_SHADER_STAGE_FRAGMENT_BIT; + } + assert_invariant(stages != 0); + + switch (binding.type) { + case DescriptorType::UNIFORM_BUFFER: { + type = (binding.flags & DescriptorFlags::DYNAMIC_OFFSET) != DescriptorFlags::NONE + ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + break; + } + case DescriptorType::SAMPLER: { + type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + break; + } + case DescriptorType::INPUT_ATTACHMENT: { + type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + break; + } + case DescriptorType::SHADER_STORAGE_BUFFER: + PANIC_POSTCONDITION("Shader storage is not supported."); + break; + } + toBind[count++] = { + .binding = binding.binding, + .descriptorType = type, + .descriptorCount = 1, + .stageFlags = stages, + }; + } + + assert_invariant(count != 0 && "Need at least one binding for descriptor set layout."); + VkDescriptorSetLayoutCreateInfo dlinfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .bindingCount = count, + .pBindings = toBind, + }; + return dlinfo; +} + +using Bitmask = VulkanDescriptorSetLayout::Bitmask; +Bitmask fromBackendLayout(DescriptorSetLayout const& layout) { + Bitmask mask; + for (auto const& binding: layout.bindings) { + switch (binding.type) { + case DescriptorType::UNIFORM_BUFFER: { + if ((binding.flags & DescriptorFlags::DYNAMIC_OFFSET) != DescriptorFlags::NONE) { + mask.dynamicUbo |= fromStageFlags(binding.stageFlags, + binding.binding); + } else { + mask.ubo |= fromStageFlags(binding.stageFlags, + binding.binding); + } + break; + } + case DescriptorType::SAMPLER: { + mask.sampler |= fromStageFlags(binding.stageFlags, binding.binding); + break; + } + case DescriptorType::INPUT_ATTACHMENT: { + mask.inputAttachment |= + fromStageFlags(binding.stageFlags, binding.binding); + break; + } + case DescriptorType::SHADER_STORAGE_BUFFER: + PANIC_POSTCONDITION("Shader storage is not supported"); + break; + } + } + return mask; +} + +} // anonymous namespace VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VkDevice device, - VkDescriptorSetLayoutCreateInfo const& info, Bitmask const& bitmask) - : VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT), - mDevice(device), - vklayout(createDescriptorSetLayout(device, info)), - bitmask(bitmask), - bindings(getBindings(bitmask)), - count(Count::fromLayoutBitmask(bitmask)) {} + DescriptorSetLayout const& layout) + : VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT), mDevice(device), + vklayout(createDescriptorSetLayout(device, getLayoutCreateInfo(layout))), + bitmask(fromBackendLayout(layout)), bindings(getBindings(bitmask)), + count(Count::fromLayoutBitmask(bitmask)) { +} VulkanDescriptorSetLayout::~VulkanDescriptorSetLayout() { vkDestroyDescriptorSetLayout(mDevice, vklayout, VKALLOC); } +void VulkanDescriptorSet::acquire(VulkanTexture* texture) { + mResources.acquire(texture); + mTextures[mTextureCount++] = texture; +} + +void VulkanDescriptorSet::acquire(VulkanBufferObject* bufferObject) { + mResources.acquire(bufferObject); +} + PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept { mRangeCount = 0; for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) { @@ -190,24 +242,11 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept mInfo(new(std::nothrow) PipelineInfo(builder)), mDevice(device) { - constexpr uint8_t UBO_MODULE_OFFSET = (sizeof(UniformBufferBitmask) * 8) / MAX_SHADER_MODULES; - constexpr uint8_t SAMPLER_MODULE_OFFSET = (sizeof(SamplerBitmask) * 8) / MAX_SHADER_MODULES; - constexpr uint8_t INPUT_ATTACHMENT_MODULE_OFFSET = - (sizeof(InputAttachmentBitmask) * 8) / MAX_SHADER_MODULES; - Program::ShaderSource const& blobs = builder.getShadersSource(); auto& modules = mInfo->shaders; - auto const& specializationConstants = builder.getSpecializationConstants(); - std::vector shader; - // TODO: this will be moved out of the shader as the descriptor set layout will be provided by - // Filament instead of parsed from the shaders. See [GDSR] in VulkanDescriptorSetManager.h - UniformBufferBitmask uboMask = 0; - SamplerBitmask samplerMask = 0; - InputAttachmentBitmask inputAttachmentMask = 0; - static_assert(static_cast(0) == ShaderStage::VERTEX && static_cast(1) == ShaderStage::FRAGMENT && MAX_SHADER_MODULES == 2); @@ -224,12 +263,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept dataSize = shader.size() * 4; } - auto const [ubo, sampler, inputAttachment] = getProgramBindings(blob); - uboMask |= (static_cast(ubo) << (UBO_MODULE_OFFSET * i)); - samplerMask |= (static_cast(sampler) << (SAMPLER_MODULE_OFFSET * i)); - inputAttachmentMask |= (static_cast(inputAttachment) - << (INPUT_ATTACHMENT_MODULE_OFFSET * i)); - VkShaderModule& module = modules[i]; VkShaderModuleCreateInfo moduleInfo = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, @@ -257,40 +290,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept #endif } - LayoutDescriptionList& layouts = mInfo->layouts; - layouts[0].bindings = utils::FixedCapacityVector::with_capacity( - countBits(collapseStages(uboMask))); - layouts[1].bindings = utils::FixedCapacityVector::with_capacity( - countBits(collapseStages(samplerMask))); - layouts[2].bindings = utils::FixedCapacityVector::with_capacity( - countBits(collapseStages(inputAttachmentMask))); - - addDescriptors(uboMask, layouts[0].bindings); - addDescriptors(samplerMask, layouts[1].bindings); - addDescriptors(inputAttachmentMask, layouts[2].bindings); - -#if FVK_ENABLED_DEBUG_SAMPLER_NAME - auto& bindingToName = mInfo->bindingToName; -#endif - -// backend::SamplerGroupInfo& groupInfo = builder.getSamplerGroupInfo(); -// auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; -// auto& bindings = mInfo->bindings; -// for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) { -// auto const& group = groupInfo[groupInd]; -// auto const& samplers = group.samplers; -// for (size_t i = 0; i < samplers.size(); ++i) { -// uint32_t const binding = samplers[i].binding; -// bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i); -// assert_invariant(bindings.find(binding) == bindings.end()); -// bindings.insert(binding); -// -//#if FVK_ENABLED_DEBUG_SAMPLER_NAME -// bindingToName[binding] = samplers[i].name.c_str(); -//#endif -// } -// } - #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0] << ", " << modules[1] << ")" << utils::io::endl; @@ -534,15 +533,7 @@ bool VulkanTimerQuery::isCompleted() noexcept { // timestamp has at least been written into a processed command buffer. // This fence indicates that the corresponding buffer has been completed. - if (!mFence) { - return false; - } - VkResult status = mFence->status.load(std::memory_order_relaxed); - if (status != VK_SUCCESS) { - return false; - } - - return true; + return mFence && mFence->getStatus() == VK_SUCCESS; } VulkanTimerQuery::~VulkanTimerQuery() = default; @@ -558,35 +549,4 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAl mResources.acquire(indexBuffer); } -using Bitmask = VulkanDescriptorSetLayout::Bitmask; - -Bitmask Bitmask::fromBackendLayout(descset::DescriptorSetLayout const& layout) { - Bitmask mask; - for (auto const& binding: layout.bindings) { - switch (binding.type) { - case descset::DescriptorType::UNIFORM_BUFFER: { - if (binding.flags == descset::DescriptorFlags::DYNAMIC_OFFSET) { - mask.dynamicUbo |= fromStageFlags(binding.stageFlags, - binding.binding); - } else { - mask.ubo |= fromStageFlags(binding.stageFlags, - binding.binding); - } - break; - } - case descset::DescriptorType::SAMPLER: { - mask.sampler |= fromStageFlags(binding.stageFlags, binding.binding); - break; - } - case descset::DescriptorType::INPUT_ATTACHMENT: { - mask.inputAttachment |= - fromStageFlags(binding.stageFlags, binding.binding); - break; - } - } - } - return mask; -} - - } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 52032a16a19..bb1f351fd1e 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -38,9 +38,14 @@ namespace filament::backend { using namespace descset; class VulkanTimestamps; +struct VulkanBufferObject; + +struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout { + static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 4; + static constexpr uint8_t MAX_BINDINGS = 25; -struct VulkanDescriptorSetLayout : public VulkanResource { - static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 3; + using DescriptorSetLayoutArray = std::array; // The bitmask representation of a set layout. struct Bitmask { @@ -51,16 +56,12 @@ struct VulkanDescriptorSetLayout : public VulkanResource { // Because we're using this struct as hash key, must make it's 8-bytes aligned, with no // unaccounted bytes. - uint8_t padding0 = 0; // 1 bytes - uint16_t padding1 = 0;// 2 bytes - uint32_t padding2 = 0;// 4 bytes + uint8_t padding[7] = {}; // 7 bytes bool operator==(Bitmask const& right) const { return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler && inputAttachment == right.inputAttachment; } - - static Bitmask fromBackendLayout(descset::DescriptorSetLayout const& layout); }; // This is a convenience struct to quickly check layout compatibility in terms of descriptor set @@ -71,6 +72,10 @@ struct VulkanDescriptorSetLayout : public VulkanResource { uint32_t sampler = 0; uint32_t inputAttachment = 0; + inline uint32_t total() const { + return ubo + dynamicUbo + sampler + inputAttachment; + } + bool operator==(Count const& right) const noexcept { return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler && inputAttachment == right.inputAttachment; @@ -99,8 +104,7 @@ struct VulkanDescriptorSetLayout : public VulkanResource { static_assert(sizeof(Bitmask) % 8 == 0); - explicit VulkanDescriptorSetLayout(VkDevice device, VkDescriptorSetLayoutCreateInfo const& info, - Bitmask const& bitmask); + VulkanDescriptorSetLayout(VkDevice device, DescriptorSetLayout const& layout); ~VulkanDescriptorSetLayout(); @@ -147,20 +151,17 @@ struct VulkanDescriptorSetLayout : public VulkanResource { } }; -using VulkanDescriptorSetLayoutList = std::array, - VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>; - -struct VulkanDescriptorSet : public VulkanResource { +struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet { public: // Because we need to recycle descriptor sets not used, we allow for a callback that the "Pool" // can use to repackage the vk handle. using OnRecycle = std::function; - VulkanDescriptorSet(VulkanResourceAllocator* allocator, - VkDescriptorSet rawSet, OnRecycle&& onRecycleFn) + VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet, + OnRecycle&& onRecycleFn) : VulkanResource(VulkanResourceType::DESCRIPTOR_SET), - resources(allocator), vkSet(rawSet), + mResources(allocator), mOnRecycleFn(std::move(onRecycleFn)) {} ~VulkanDescriptorSet() { @@ -169,17 +170,25 @@ struct VulkanDescriptorSet : public VulkanResource { } } + void acquire(VulkanTexture* texture); + + void acquire(VulkanBufferObject* texture); + + bool hasTexture(VulkanTexture* texture) { + return std::any_of(mTextures.begin(), mTextures.end(), + [texture](auto t) { return t == texture; }); + } + // TODO: maybe change to fixed size for performance. - VulkanAcquireOnlyResourceManager resources; VkDescriptorSet const vkSet; private: + std::array mTextures = { nullptr }; + uint8_t mTextureCount = 0; + VulkanAcquireOnlyResourceManager mResources; OnRecycle mOnRecycleFn; }; -using VulkanDescriptorSetList = std::array, - VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>; - using PushConstantNameArray = utils::FixedCapacityVector; using PushConstantNameByStage = std::array; @@ -224,16 +233,6 @@ struct VulkanProgram : public HwProgram, VulkanResource { // samplers. inline BindingList const& getBindings() const { return mInfo->bindings; } - // TODO: this is currently not used. This will replace getLayoutDescriptionList below. - // inline descset::DescriptorSetLayout const& getLayoutDescription() const { - // return mInfo->layout; - // } - // In the usual case, we would have just one layout per program. But in the current setup, we - // have a set/layout for each descriptor type. This will be changed in the future. - using LayoutDescriptionList = std::array; - inline LayoutDescriptionList const& getLayoutDescriptionList() const { return mInfo->layouts; } - inline uint32_t getPushConstantRangeCount() const { return mInfo->pushConstantDescription.getVkRangeCount(); } @@ -273,10 +272,6 @@ struct VulkanProgram : public HwProgram, VulkanResource { utils::FixedCapacityVector bindingToSamplerIndex; VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE }; - // TODO: Use this instead of `layouts` after Filament-side Descriptor Set API is in place. - // descset::DescriptorSetLayout layout; - LayoutDescriptionList layouts; - PushConstantDescription pushConstantDescription; #if FVK_ENABLED_DEBUG_SAMPLER_NAME @@ -482,11 +477,6 @@ struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource { utils::Mutex mFenceMutex; }; -struct VulkanDescriptorSetLayout2 : public HwDescriptorSetLayout, VulkanResource { -}; - -struct VulkanDescriptorSet2 : public HwDescriptorSet, VulkanResource { -}; inline constexpr VkBufferUsageFlagBits getBufferObjectUsage( BufferObjectBinding bindingType) noexcept { diff --git a/filament/backend/src/vulkan/VulkanImageUtility.cpp b/filament/backend/src/vulkan/VulkanImageUtility.cpp index 6c038e429f0..415eb305cab 100644 --- a/filament/backend/src/vulkan/VulkanImageUtility.cpp +++ b/filament/backend/src/vulkan/VulkanImageUtility.cpp @@ -131,14 +131,18 @@ getVkTransition(const VulkanLayoutTransition& transition) { }// anonymous namespace -void transitionLayout(VkCommandBuffer cmdbuffer, +bool transitionLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition) { if (transition.oldLayout == transition.newLayout) { - return; + return false; } auto [srcAccessMask, dstAccessMask, srcStage, dstStage, oldLayout, newLayout] = getVkTransition(transition); + if (oldLayout == newLayout) { + return false; + } + assert_invariant(transition.image != VK_NULL_HANDLE && "No image for transition"); VkImageMemoryBarrier barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -152,6 +156,7 @@ void transitionLayout(VkCommandBuffer cmdbuffer, .subresourceRange = transition.subresources, }; vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier); + return true; } }// namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanImageUtility.h b/filament/backend/src/vulkan/VulkanImageUtility.h index 92aaac96ea4..82ff436c163 100644 --- a/filament/backend/src/vulkan/VulkanImageUtility.h +++ b/filament/backend/src/vulkan/VulkanImageUtility.h @@ -135,7 +135,9 @@ constexpr inline VkImageLayout getVkLayout(VulkanLayout layout) { } } -void transitionLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition); +// Returns true if a transition has been added to the command buffer, false otherwis (where there is +// no transition necessary). +bool transitionLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition); } // namespace imgutil diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index 4c77525bd1b..27f70e7840d 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -98,7 +98,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n VkPipelineColorBlendStateCreateInfo colorBlendState; colorBlendState = VkPipelineColorBlendStateCreateInfo{}; colorBlendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendState.attachmentCount = 1; + colorBlendState.attachmentCount = mPipelineRequirements.rasterState.colorTargetCount; colorBlendState.pAttachments = colorBlendAttachments; // If we reach this point, we need to create and stash a brand new pipeline object. @@ -209,8 +209,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n pipelineCreateInfo.pDynamicState = &dynamicState; // Filament assumes consistent blend state across all color attachments. - colorBlendState.attachmentCount = mPipelineRequirements.rasterState.colorTargetCount; - for (auto& target : colorBlendAttachments) { + for (uint8_t i = 0; i < colorBlendState.attachmentCount; ++i) { + auto& target = colorBlendAttachments[i]; target.blendEnable = mPipelineRequirements.rasterState.blendEnable; target.srcColorBlendFactor = mPipelineRequirements.rasterState.srcColorBlendFactor; target.dstColorBlendFactor = mPipelineRequirements.rasterState.dstColorBlendFactor; diff --git a/filament/backend/src/vulkan/VulkanReadPixels.cpp b/filament/backend/src/vulkan/VulkanReadPixels.cpp index be43e2ae607..bef08811c6d 100644 --- a/filament/backend/src/vulkan/VulkanReadPixels.cpp +++ b/filament/backend/src/vulkan/VulkanReadPixels.cpp @@ -232,7 +232,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint VulkanAttachment const srcAttachment = srcTarget->getColor(0); VkImageSubresourceRange const srcRange = srcAttachment.getSubresourceRange(); - srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC); + srcTexture->transitionLayout(cmdbuffer, {}, srcRange, VulkanLayout::TRANSFER_SRC); VkImageCopy const imageCopyRegion = { .srcSubresource = { @@ -268,7 +268,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint imgutil::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion); // Restore the source image layout. - srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::COLOR_ATTACHMENT); + srcTexture->transitionLayout(cmdbuffer, {}, srcRange, VulkanLayout::COLOR_ATTACHMENT); vkEndCommandBuffer(cmdbuffer); diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index 8c4a345caec..0cf75d0f13e 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -75,7 +75,7 @@ void VulkanSwapChain::update() { void VulkanSwapChain::present() { if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) { - VkCommandBuffer const cmdbuf = mCommands->get().buffer(); + VulkanCommandBuffer& commands = mCommands->get(); VkImageSubresourceRange const subresources{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, @@ -83,7 +83,7 @@ void VulkanSwapChain::present() { .baseArrayLayer = 0, .layerCount = 1, }; - mColors[mCurrentSwapIndex]->transitionLayout(cmdbuf, subresources, VulkanLayout::PRESENT); + mColors[mCurrentSwapIndex]->transitionLayout(&commands, subresources, VulkanLayout::PRESENT); } mCommands->flush(); diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index adf0f4ad9ef..3d021daff0d 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -226,17 +226,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, // Go ahead and create the primary image view. getImageView(mPrimaryViewRange, mViewType, mSwizzle); - // Transition the layout of each image slice that might be used as a render target. - // We do not transition images that are merely SAMPLEABLE, this is deferred until upload time - // because we do not know how many layers and levels will actually be used. - if (imageInfo.usage - & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT - | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - VulkanCommandBuffer& commands = mCommands->get(); - VkCommandBuffer const cmdbuf = commands.buffer(); - commands.acquire(this); - transitionLayout(cmdbuf, mFullViewRange, imgutil::getDefaultLayout(imageInfo.usage)); - } + VulkanCommandBuffer& commandsBuf = mCommands->get(); + commandsBuf.acquire(this); + transitionLayout(&commandsBuf, mFullViewRange, imgutil::getDefaultLayout(imageInfo.usage)); } VulkanTexture::~VulkanTexture() { @@ -254,7 +246,6 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt assert_invariant(width <= this->width && height <= this->height); assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP || target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1)); - const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; @@ -332,11 +323,11 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt nextLayout = imgutil::getDefaultLayout(this->usage); } - transitionLayout(cmdbuf, transitionRange, newLayout); + transitionLayout(&commands, transitionRange, newLayout); vkCmdCopyBufferToImage(cmdbuf, stage->buffer, mTextureImage, newVkLayout, 1, ©Region); - transitionLayout(cmdbuf, transitionRange, nextLayout); + transitionLayout(&commands, transitionRange, nextLayout); } void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, @@ -371,12 +362,12 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST; VulkanLayout const oldLayout = getLayout(layer, miplevel); - transitionLayout(cmdbuf, range, newLayout); + transitionLayout(&commands, range, newLayout); vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC), mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST); - transitionLayout(cmdbuf, range, oldLayout); + transitionLayout(&commands, range, oldLayout); } void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel) { @@ -425,9 +416,15 @@ VkImageAspectFlags VulkanTexture::getImageAspect() const { return filament::backend::getImageAspect(mVkFormat); } -void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubresourceRange& range, - VulkanLayout newLayout) { +void VulkanTexture::transitionLayout(VulkanCommandBuffer* commands, + const VkImageSubresourceRange& range, VulkanLayout newLayout) { + transitionLayout(commands->buffer(), commands->fence, range, newLayout); +} +void VulkanTexture::transitionLayout( + VkCommandBuffer cmdbuf, std::shared_ptr fence, + const VkImageSubresourceRange& range, + VulkanLayout newLayout) { VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); uint32_t const firstLayer = range.baseArrayLayer; @@ -438,7 +435,7 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres // If we are transitioning more than one layer/level (slice), we need to know whether they are // all of the same layer. If not, we need to transition slice-by-slice. Otherwise it would // trigger the validation layer saying that the `oldLayout` provided is incorrect. - // TODO: transition by multiple slices with more sophiscated range finding. + // TODO: transition by multiple slices with more sophisticated range finding. bool transitionSliceBySlice = false; for (uint32_t i = firstLayer; i < lastLayer; ++i) { for (uint32_t j = firstLevel; j < lastLevel; ++j) { @@ -449,39 +446,30 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres } } -#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) - FVK_LOGD << "transition texture=" << mTextureImage - << " (" << range.baseArrayLayer - << "," << range.baseMipLevel << ")" - << " count=(" << range.layerCount - << "," << range.levelCount << ")" - << " from=" << oldLayout << " to=" << newLayout - << " format=" << mVkFormat - << " depth=" << isVkDepthFormat(mVkFormat) - << " slice-by-slice=" << transitionSliceBySlice - << utils::io::endl; -#endif - + bool hasTransitions = false; if (transitionSliceBySlice) { for (uint32_t i = firstLayer; i < lastLayer; ++i) { for (uint32_t j = firstLevel; j < lastLevel; ++j) { VulkanLayout const layout = getLayout(i, j); - imgutil::transitionLayout(cmdbuf, { - .image = mTextureImage, - .oldLayout = layout, - .newLayout = newLayout, - .subresources = { - .aspectMask = range.aspectMask, - .baseMipLevel = j, - .levelCount = 1, - .baseArrayLayer = i, - .layerCount = 1, - }, - }); + if (layout == newLayout) { + continue; + } + hasTransitions = hasTransitions || imgutil::transitionLayout(cmdbuf, { + .image = mTextureImage, + .oldLayout = layout, + .newLayout = newLayout, + .subresources = { + .aspectMask = range.aspectMask, + .baseMipLevel = j, + .levelCount = 1, + .baseArrayLayer = i, + .layerCount = 1, + }, + }); } } - } else { - imgutil::transitionLayout(cmdbuf, { + } else if (newLayout != oldLayout) { + hasTransitions = imgutil::transitionLayout(cmdbuf, { .image = mTextureImage, .oldLayout = oldLayout, .newLayout = newLayout, @@ -489,7 +477,25 @@ void VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, const VkImageSubres }); } - setLayout(range, newLayout); + if (hasTransitions) { + mTransitionFence = fence; + setLayout(range, newLayout); + +#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) + FVK_LOGD << "transition texture=" << mTextureImage << " (" << range.baseArrayLayer + << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," + << range.levelCount << ")" << " from=" << oldLayout << " to=" << newLayout + << " format=" << mVkFormat << " depth=" << isVkDepthFormat(mVkFormat) + << " slice-by-slice=" << transitionSliceBySlice << utils::io::endl; +#endif + } else { +#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) + FVK_LOGD << "transition texture=" << mTextureImage << " (" << range.baseArrayLayer + << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," + << range.levelCount << ")" << " to=" << newLayout + << " is skipped because of no change in layout" << utils::io::endl; +#endif + } } void VulkanTexture::setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout) { diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index 7b0b64a8c4c..6b19a968320 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -92,9 +92,12 @@ struct VulkanTexture : public HwTexture, VulkanResource { return mSidecarMSAA.get(); } - void transitionLayout(VkCommandBuffer commands, const VkImageSubresourceRange& range, + void transitionLayout(VulkanCommandBuffer* commands, const VkImageSubresourceRange& range, VulkanLayout newLayout); + void transitionLayout(VkCommandBuffer cmdbuf, std::shared_ptr fence, + const VkImageSubresourceRange& range, VulkanLayout newLayout); + // Returns the preferred data plane of interest for all image views. // For now this always returns either DEPTH or COLOR. VkImageAspectFlags getImageAspect() const; @@ -103,12 +106,19 @@ struct VulkanTexture : public HwTexture, VulkanResource { // manually (outside of calls to transitionLayout). void setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout); + void setPrimaryLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout); + + bool transitionReady() { + auto res = !mTransitionFence || mTransitionFence->getStatus() == VK_SUCCESS; + mTransitionFence.reset(); + return res; + } + #if FVK_ENABLED(FVK_DEBUG_TEXTURE) void print() const; #endif private: - struct ImageViewKey { VkImageSubresourceRange range; // 4 * 5 bytes VkImageViewType type; // 4 bytes @@ -160,6 +170,7 @@ struct VulkanTexture : public HwTexture, VulkanResource { VkDevice mDevice; VmaAllocator mAllocator; VulkanCommands* mCommands; + std::shared_ptr mTransitionFence; }; } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanUtility.h b/filament/backend/src/vulkan/VulkanUtility.h index cb780cf8857..e0de22cf4dc 100644 --- a/filament/backend/src/vulkan/VulkanUtility.h +++ b/filament/backend/src/vulkan/VulkanUtility.h @@ -405,12 +405,13 @@ constexpr VkFormat ALL_VK_FORMATS[] = { VK_FORMAT_R16G16_S10_5_NV, }; -// An Array that will be fixed capacity, but the "size" (as in user added elements) is variable. -// Note that this class is movable. +// An Array that will be statically fixed in capacity, but the "size" (as in user added elements) is +// variable. Note that this class is movable. template class CappedArray { private: using FixedSizeArray = std::array; + public: using const_iterator = typename FixedSizeArray::const_iterator; using iterator = typename FixedSizeArray::iterator; @@ -448,6 +449,20 @@ class CappedArray { return mArray.cend(); } + inline iterator begin() { + if (mInd == 0) { + return mArray.end(); + } + return mArray.begin(); + } + + inline iterator end() { + if (mInd > 0 && mInd < CAPACITY) { + return mArray.begin() + mInd; + } + return mArray.end(); + } + inline T back() { assert_invariant(mInd > 0); return *(mArray.begin() + mInd); @@ -515,15 +530,12 @@ class CappedArray { // TODO: ok to remove once Filament-side API is complete namespace descset { -// Used to describe the descriptor binding in shader stages. We assume that the binding index does -// not exceed 31. We also assume that we have two shader stages - vertex and fragment. The below -// types and struct are used across VulkanDescriptorSet and VulkanProgram. -using UniformBufferBitmask = uint32_t; +using UniformBufferBitmask = uint64_t; using SamplerBitmask = uint64_t; // We only have at most one input attachment, so this bitmask exists only to make the code more // general. -using InputAttachmentBitmask = uint8_t; +using InputAttachmentBitmask = uint64_t; constexpr UniformBufferBitmask UBO_VERTEX_STAGE = 0x1; constexpr UniformBufferBitmask UBO_FRAGMENT_STAGE = (0x1ULL << (sizeof(UniformBufferBitmask) * 4)); @@ -533,6 +545,7 @@ constexpr InputAttachmentBitmask INPUT_ATTACHMENT_VERTEX_STAGE = 0x1; constexpr InputAttachmentBitmask INPUT_ATTACHMENT_FRAGMENT_STAGE = (0x1ULL << (sizeof(InputAttachmentBitmask) * 4)); + template static constexpr Bitmask getVertexStage() noexcept { if constexpr (std::is_same_v) { @@ -559,35 +572,6 @@ static constexpr Bitmask getFragmentStage() noexcept { } } -typedef enum ShaderStageFlags2 : uint8_t { - NONE = 0, - VERTEX = 0x1, - FRAGMENT = 0x2, -} ShaderStageFlags2; - -enum class DescriptorType : uint8_t { - UNIFORM_BUFFER, - SAMPLER, - INPUT_ATTACHMENT, -}; - -enum class DescriptorFlags : uint8_t { - NONE = 0x00, - DYNAMIC_OFFSET = 0x01 -}; - -struct DescriptorSetLayoutBinding { - DescriptorType type; - ShaderStageFlags2 stageFlags; - uint8_t binding; - DescriptorFlags flags; - uint16_t count; -}; - -struct DescriptorSetLayout { - utils::FixedCapacityVector bindings; -}; - } // namespace descset namespace { diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp index 16b53b11566..331a674b612 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp @@ -34,19 +34,9 @@ namespace filament::backend { namespace { -// This assumes we have at most 32-bound samplers, 10 UBOs and, 1 input attachment. -// TODO: Obsolete after [GDSR]. -constexpr uint8_t MAX_SAMPLER_BINDING = 32; -constexpr uint8_t MAX_UBO_BINDING = 10; -constexpr uint8_t MAX_INPUT_ATTACHMENT_BINDING = 1; -constexpr uint8_t MAX_BINDINGS = - MAX_SAMPLER_BINDING + MAX_UBO_BINDING + MAX_INPUT_ATTACHMENT_BINDING; - using Bitmask = VulkanDescriptorSetLayout::Bitmask; using DescriptorCount = VulkanDescriptorSetLayout::Count; -using UBOMap = std::array, MAX_UBO_BINDING>; -using SamplerMap = - std::array, MAX_SAMPLER_BINDING>; +using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray; using BitmaskHashFn = utils::hash::MurmurHashFn; struct BitmaskEqual { bool operator()(Bitmask const& k1, Bitmask const& k2) const { @@ -71,15 +61,12 @@ struct BitmaskEqual { // single pool without too much waste. class DescriptorPool { public: - DescriptorPool(VkDevice device, VulkanResourceAllocator* allocator, - DescriptorCount const& count, uint16_t capacity) + DescriptorPool(VkDevice device, DescriptorCount const& count, uint16_t capacity) : mDevice(device), - mAllocator(allocator), mCount(count), mCapacity(capacity), mSize(0), - mUnusedCount(0), - mDisableRecycling(false) { + mUnusedCount(0) { DescriptorCount const actual = mCount * capacity; VkDescriptorPoolSize sizes[4]; uint8_t npools = 0; @@ -122,42 +109,32 @@ class DescriptorPool { DescriptorPool& operator=(DescriptorPool const&) = delete; ~DescriptorPool() { - // Note that these have to manually destroyed because they were not explicitly ref-counted. - for (auto const& [mask, sets]: mUnused) { - for (auto set: sets) { - mAllocator->destruct(set); - } - } vkDestroyDescriptorPool(mDevice, mPool, VKALLOC); } - void disableRecycling() noexcept { - mDisableRecycling = true; - } - uint16_t const& capacity() { return mCapacity; } // A convenience method for checking if this pool can allocate sets for a given layout. - inline bool canAllocate(VulkanDescriptorSetLayout* layout) { - return layout->count == mCount; + inline bool canAllocate(DescriptorCount const& count) { + return count == mCount; } - Handle obtainSet(VulkanDescriptorSetLayout* layout) { + VkDescriptorSet obtainSet(VulkanDescriptorSetLayout* layout) { if (UnusedSetMap::iterator itr = mUnused.find(layout->bitmask); itr != mUnused.end()) { // If we don't have any unused, then just return an empty handle. if (itr->second.empty()) { - return {}; + return VK_NULL_HANDLE; } - std::vector>& sets = itr->second; + std::vector& sets = itr->second; auto set = sets.back(); sets.pop_back(); mUnusedCount--; return set; } if (mSize + 1 > mCapacity) { - return {}; + return VK_NULL_HANDLE; } // Creating a new set VkDescriptorSetLayout layouts[1] = {layout->vklayout}; @@ -174,31 +151,23 @@ class DescriptorPool { << "Failed to allocate descriptor set code=" << result << " size=" << mSize << " capacity=" << mCapacity << " count=" << "%s"; mSize++; - return createSet(layout->bitmask, vkSet); + return vkSet; } -private: - Handle createSet(Bitmask const& layoutMask, VkDescriptorSet vkSet) { - return mAllocator->initHandle(mAllocator, vkSet, - [this, layoutMask, vkSet]() { - if (mDisableRecycling) { - return; - } - // We are recycling - release the set back into the pool. Note that the - // vk handle has not changed, but we need to change the backend handle to allow - // for proper refcounting of resources referenced in this set. - auto setHandle = createSet(layoutMask, vkSet); - if (auto itr = mUnused.find(layoutMask); itr != mUnused.end()) { - itr->second.push_back(setHandle); - } else { - mUnused[layoutMask].push_back(setHandle); - } - mUnusedCount++; - }); + void recycle(Bitmask const& layoutMask, VkDescriptorSet vkSet) { + // We are recycling - release the set back into the pool. Note that the + // vk handle has not changed, but we need to change the backend handle to allow + // for proper refcounting of resources referenced in this set. + if (auto itr = mUnused.find(layoutMask); itr != mUnused.end()) { + itr->second.push_back(vkSet); + } else { + mUnused[layoutMask].push_back(vkSet); + } + mUnusedCount++; } +private: VkDevice mDevice; - VulkanResourceAllocator* mAllocator; VkDescriptorPool mPool; DescriptorCount const mCount; uint16_t const mCapacity; @@ -209,11 +178,9 @@ class DescriptorPool { uint16_t mUnusedCount; // This maps a layout ot a list of descriptor sets allocated for that layout. - using UnusedSetMap = std::unordered_map>, - BitmaskHashFn, BitmaskEqual>; + using UnusedSetMap = + std::unordered_map, BitmaskHashFn, BitmaskEqual>; UnusedSetMap mUnused; - - bool mDisableRecycling; }; // This is an ever-expanding pool of sets where it @@ -225,17 +192,16 @@ class DescriptorInfinitePool { static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5; public: - DescriptorInfinitePool(VkDevice device, VulkanResourceAllocator* allocator) - : mDevice(device), - mAllocator(allocator) {} + DescriptorInfinitePool(VkDevice device) + : mDevice(device) {} - Handle obtainSet(VulkanDescriptorSetLayout* layout) { + VkDescriptorSet obtainSet(VulkanDescriptorSetLayout* layout) { DescriptorPool* sameTypePool = nullptr; for (auto& pool: mPools) { - if (!pool->canAllocate(layout)) { + if (!pool->canAllocate(layout->count)) { continue; } - if (auto set = pool->obtainSet(layout); set) { + if (auto set = pool->obtainSet(layout); set != VK_NULL_HANDLE) { return set; } if (!sameTypePool || sameTypePool->capacity() < pool->capacity()) { @@ -250,121 +216,27 @@ class DescriptorInfinitePool { } // We need to increase the set of pools by one. - mPools.push_back(std::make_unique(mDevice, mAllocator, + mPools.push_back(std::make_unique(mDevice, DescriptorCount::fromLayoutBitmask(layout->bitmask), capacity)); auto& pool = mPools.back(); auto ret = pool->obtainSet(layout); - assert_invariant(ret && "failed to obtain a set?"); + assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?"); return ret; } - void disableRecycling() noexcept { + void recycle(DescriptorCount const& count, Bitmask const& layoutMask, VkDescriptorSet vkSet) { for (auto& pool: mPools) { - pool->disableRecycling(); - } - } - -private: - VkDevice mDevice; - VulkanResourceAllocator* mAllocator; - std::vector> mPools; -}; - -class LayoutCache { -private: - using Key = Bitmask; - - // Make sure the key is 8-bytes aligned. - static_assert(sizeof(Key) % 8 == 0); - - using LayoutMap = std::unordered_map, BitmaskHashFn, - BitmaskEqual>; - -public: - explicit LayoutCache(VkDevice device, VulkanResourceAllocator* allocator) - : mDevice(device), - mAllocator(allocator) {} - - ~LayoutCache() { - for (auto [key, layout]: mLayouts) { - mAllocator->destruct(layout); - } - mLayouts.clear(); - } - - void destroyLayout(Handle handle) { - for (auto [key, layout]: mLayouts) { - if (layout == handle) { - mLayouts.erase(key); - break; - } - } - mAllocator->destruct(handle); - } - - Handle getLayout(descset::DescriptorSetLayout const& layout) { - Key key = Bitmask::fromBackendLayout(layout); - if (auto iter = mLayouts.find(key); iter != mLayouts.end()) { - return iter->second; - } - - VkDescriptorSetLayoutBinding toBind[MAX_BINDINGS]; - uint32_t count = 0; - - for (auto const& binding: layout.bindings) { - VkShaderStageFlags stages = 0; - VkDescriptorType type; - - if (binding.stageFlags & descset::ShaderStageFlags2::VERTEX) { - stages |= VK_SHADER_STAGE_VERTEX_BIT; - } - if (binding.stageFlags & descset::ShaderStageFlags2::FRAGMENT) { - stages |= VK_SHADER_STAGE_FRAGMENT_BIT; - } - assert_invariant(stages != 0); - - switch (binding.type) { - case descset::DescriptorType::UNIFORM_BUFFER: { - type = binding.flags == descset::DescriptorFlags::DYNAMIC_OFFSET - ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC - : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - break; - } - case descset::DescriptorType::SAMPLER: { - type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - break; - } - case descset::DescriptorType::INPUT_ATTACHMENT: { - type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - break; - } + if (!pool->canAllocate(count)) { + continue; } - toBind[count++] = { - .binding = binding.binding, - .descriptorType = type, - .descriptorCount = 1, - .stageFlags = stages, - }; - } - - if (count == 0) { - return {}; + pool->recycle(layoutMask, vkSet); + break; } - - VkDescriptorSetLayoutCreateInfo dlinfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .bindingCount = count, - .pBindings = toBind, - }; - return (mLayouts[key] = - mAllocator->initHandle(mDevice, dlinfo, key)); } private: VkDevice mDevice; - VulkanResourceAllocator* mAllocator; - LayoutMap mLayouts; + std::vector> mPools; }; template @@ -374,632 +246,208 @@ struct Equal { } }; -// TODO: Obsolete after [GDSR]. -// No need to cache afterwards. -struct UBOKey { - uint8_t count; - uint8_t padding[5]; - uint8_t bindings[MAX_UBO_BINDING]; - // Note that the number of bytes for above is 1 + 5 + 10 = 16, which is divisible by 8. - static_assert((sizeof(count) + sizeof(padding) + sizeof(bindings)) % 8 == 0); - - VkBuffer buffers[MAX_UBO_BINDING]; - VkDeviceSize offsets[MAX_UBO_BINDING]; - VkDeviceSize sizes[MAX_UBO_BINDING]; - - static inline UBOKey key(UBOMap const& uboMap, VulkanDescriptorSetLayout* layout) { - UBOKey ret{ - .count = (uint8_t) layout->count.ubo, - }; - uint8_t count = 0; - for (uint8_t binding: layout->bindings.ubo) { - auto const& [info, obj] = uboMap[binding]; - ret.bindings[count] = binding; - if (obj) { - ret.buffers[count] = info.buffer; - ret.offsets[count] = info.offset; - ret.sizes[count] = info.range; - }// else we keep them as VK_NULL_HANDLE and 0s. - count++; - } - return ret; - } - - using HashFn = utils::hash::MurmurHashFn; - using Equal = Equal; -}; - -// TODO: Obsolete after [GDSR]. -// No need to cache afterwards. -struct SamplerKey { - uint8_t count; - uint8_t padding[7]; - uint8_t bindings[MAX_SAMPLER_BINDING]; - static_assert(sizeof(bindings) % 8 == 0); - VkSampler sampler[MAX_SAMPLER_BINDING]; - VkImageView imageView[MAX_SAMPLER_BINDING]; - VkImageLayout imageLayout[MAX_SAMPLER_BINDING]; - - static inline SamplerKey key(SamplerMap const& samplerMap, VulkanDescriptorSetLayout* layout) { - SamplerKey ret{ - .count = (uint8_t) layout->count.sampler, - }; - uint8_t count = 0; - for (uint8_t binding: layout->bindings.sampler) { - auto const& [info, obj] = samplerMap[binding]; - ret.bindings[count] = binding; - if (obj) { - ret.sampler[count] = info.sampler; - ret.imageView[count] = info.imageView; - ret.imageLayout[count] = info.imageLayout; - } // else keep them as VK_NULL_HANDLEs. - count++; - } - return ret; - } - - using HashFn = utils::hash::MurmurHashFn; - using Equal = Equal; -}; - -// TODO: Obsolete after [GDSR]. -// No need to cache afterwards. -struct InputAttachmentKey { - // This count should be fixed. - uint8_t count; - uint8_t padding[3]; - VkImageLayout imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImageView view = VK_NULL_HANDLE; - - static inline InputAttachmentKey key(VkDescriptorImageInfo const& info, - VulkanDescriptorSetLayout* layout) { - return { - .count = (uint8_t) layout->count.inputAttachment, - .imageLayout = info.imageLayout, - .view = info.imageView, - }; - } - - using HashFn = utils::hash::MurmurHashFn; - using Equal = Equal; -}; - -// TODO: Obsolete after [GDSR]. -// No need to cache afterwards. -template -class LRUDescriptorSetCache { -private: - static constexpr size_t MINIMUM_DESCRIPTOR_SETS = 100; - static constexpr float LRU_REDUCTION_FACTOR = .8f; - static constexpr uint64_t N_FRAMES_AGO = 20; - -public: - using SetPtr = VulkanDescriptorSet*; - LRUDescriptorSetCache(VulkanResourceAllocator* allocator) - : mFrame(0), - mId(0), - mResources(allocator) {} - - void gc() { - mFrame++; - - // Never gc for the first N frames. - if (mFrame < N_FRAMES_AGO) { - return; - } - - uint64_t const nFramesAgo = (mFrame - N_FRAMES_AGO) << 32; - size_t const size = mCache.size(); - if (size < MINIMUM_DESCRIPTOR_SETS) { - return; - } - - auto const& popped = mLRU.pop((size_t) (size * LRU_REDUCTION_FACTOR), nFramesAgo); - for (auto p: popped) { - mCache.erase(p); - mResources.release(p); - } - } - - inline SetPtr get(Key const& key) { - if (auto itr = mCache.find(key); itr != mCache.end()) { - auto const& ret = itr->second; - mLRU.update(ret, (mFrame << 32) | mId++); - return ret; - } - return nullptr; - } - - void put(Key const& key, SetPtr set) { - mLRU.update(set, (mFrame << 32) | mId++); - mCache.put(key, set); - mResources.acquire(set); - } - - void erase(SetPtr set) { - mCache.erase(set); - mLRU.erase(set); - mResources.release(set); - } - - inline size_t size() const { - return mCache.size(); - } +} // anonymous namespace +class VulkanDescriptorSetManager::Impl { private: - struct BiMap { - using ForwardMap - = std::unordered_map; - - typename ForwardMap::const_iterator find(Key const& key) const { - return forward.find(key); - } - typename ForwardMap::const_iterator end() const { - return forward.end(); - } + using DescriptorSetArray = std::array; - inline size_t size() const { - return forward.size(); - } - - void erase(Key const& key) { - if (auto itr = forward.find(key); itr != forward.end()) { - auto const& ptr = itr->second; - forward.erase(key); - backward.erase(ptr); + struct DescriptorSetHistory { + private: + using TextureBundle = std::pair; + public: + DescriptorSetHistory() : mResources(nullptr) {} + + DescriptorSetHistory(Bitmask const& mask, DescriptorCount const& count, + VkDescriptorSetLayout layout, VulkanResourceAllocator* allocator, + VulkanDescriptorSet* set) + : mResources(allocator), + mSet(set), + mMask(mask), + mLayout(layout), + mCount(count), + mMaxIndex(0), + mWritten(0), + mBound(false) { + assert_invariant(mCount.total() < sizeof(mWritten) * 8); + // initial state is unbound. + unbind(); + + for (uint8_t i = 0; i < 32; ++i) { + if ((mMask.ubo | mMask.dynamicUbo | mMask.sampler | mMask.inputAttachment) & + (1LL < i)) { + mMaxIndex = i; + } } } - void erase(SetPtr ptr) { - if (auto itr = backward.find(ptr); itr != backward.end()) { - auto const& key = itr->second; - forward.erase(key); - backward.erase(ptr); + ~DescriptorSetHistory() { + if (mSet) { + mResources.clear(); } } - void put(Key const& key, SetPtr ptr) { - forward[key] = ptr; - backward[ptr] = key; + void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept { + mOffsets = std::move(offsets); + mBound = false; } - SetPtr const& get(Key const& key) { - return forward[key]; + void write(uint8_t binding) noexcept { + mWritten |= (1LL << binding); + mBound = false; } - private: - ForwardMap forward; - std::unordered_map backward; - }; - - struct PriorityQueue { - void update(SetPtr const& ptr, uint64_t priority) { - if (auto itr = backward.find(ptr); itr != backward.end()) { - auto const& priority = itr->second; - forward.erase(priority); - forward[priority] = ptr; - backward[ptr] = priority; - } else { - backward[ptr] = priority; - forward[priority] = ptr; - } + void write(uint8_t binding, VkImageSubresourceRange const& range, VulkanTexture* texture) noexcept { + write(binding); + mTextures.insert({texture, range}); } - void erase(SetPtr ptr) { - if (auto itr = backward.find(ptr); itr != backward.end()) { - auto const& priority = itr->second; - forward.erase(priority); - backward.erase(ptr); - } - } + // Ownership will be transfered to the commandbuffer. + void bind(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, uint8_t index) noexcept { + VkCommandBuffer const cmdbuffer = commands->buffer(); + vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index, 1, + &mSet->vkSet, mCount.dynamicUbo, mOffsets.data()); - void erase(uint64_t priority) { - if (auto itr = forward.find(priority); itr != forward.end()) { - auto const& ptr = itr->second; - backward.erase(ptr); - forward.erase(itr); - } + commands->acquire(mSet); + mResources.clear(); + mBound = true; } - // Pop the lowest `popCount` elements that are equal or less than `priority` - utils::FixedCapacityVector pop(size_t popCount, uint64_t priority) { - utils::FixedCapacityVector evictions - = utils::FixedCapacityVector::with_capacity(popCount); - for (auto itr = forward.begin(); itr != forward.end() && popCount > 0; - itr++, popCount--) { - auto const& [ipriority, ival] = *itr; - if (ipriority > priority) { - break; - } - evictions.push_back(ival); - } - for (auto p: evictions) { - erase(p); - } - return evictions; + void unbind() noexcept { + mResources.acquire(mSet); + mBound = false; } + bool bound() const noexcept { return mBound; } + bool written(uint8_t binding) const noexcept { return mWritten & (1LL << binding); } + VkDescriptorSetLayout layout() const noexcept { return mLayout; } + Bitmask const& mask() const noexcept { return mMask; } + uint8_t maxIndex() const noexcept { return mMaxIndex; } + private: - std::map forward; - std::unordered_map backward; + FixedSizeVulkanResourceManager<1> mResources; + VulkanDescriptorSet* mSet = nullptr; + + CappedArray mTextures; + + backend::DescriptorSetOffsetArray mOffsets; + Bitmask mMask = {}; + VkDescriptorSetLayout mLayout = VK_NULL_HANDLE; + DescriptorCount mCount = {}; + uint8_t mMaxIndex = 0; + uint64_t mWritten = 0; + bool mBound = false; }; - uint64_t mFrame; - uint64_t mId; - - BiMap mCache; - PriorityQueue mLRU; - VulkanResourceManager mResources; -}; + struct BoundInfo { + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + uint8_t setMask = 0; + DescriptorSetArray boundSets = {}; -// TODO: Obsolete after [GDSR]. -// No need to cache afterwards. -// The purpose of this class is to ensure that each descriptor set is only written to once, and can -// be re-bound if necessary. Therefore, we'll cache a set based on its content and return a cached -// set if we find a content match. -// It also uses a LRU heuristic for caching. The implementation of the heuristic is in the above -// class LRUDescriptorSetCache. -class DescriptorSetCache { -public: - DescriptorSetCache(VkDevice device, VulkanResourceAllocator* allocator) - : mAllocator(allocator), - mDescriptorPool(std::make_unique(device, allocator)), - mUBOCache(std::make_unique>(allocator)), - mSamplerCache(std::make_unique>(allocator)), - mInputAttachmentCache( - std::make_unique>(allocator)) {} - - template - inline std::pair get(Key const& key, - VulkanDescriptorSetLayout* layout) { - if constexpr (std::is_same_v) { - return get(key, *mUBOCache, layout); - } else if constexpr (std::is_same_v) { - return get(key, *mSamplerCache, layout); - } else if constexpr (std::is_same_v) { - return get(key, *mInputAttachmentCache, layout); - } - PANIC_POSTCONDITION("Unexpected key type"); - } - - ~DescriptorSetCache() { - // This will prevent the descriptor sets recycling when we destroy descriptor set caches. - mDescriptorPool->disableRecycling(); - - mInputAttachmentCache.reset(); - mSamplerCache.reset(); - mUBOCache.reset(); - mDescriptorPool.reset(); - } - - // gc() should be called at the end of everyframe - void gc() { - mUBOCache->gc(); - mSamplerCache->gc(); - mInputAttachmentCache->gc(); - } - -private: - template - inline std::pair get(Key const& key, - LRUDescriptorSetCache& cache, VulkanDescriptorSetLayout* layout) { - if (auto set = cache.get(key); set) { - return {set, true}; - } - auto set = mAllocator->handle_cast( - mDescriptorPool->obtainSet(layout)); - cache.put(key, set); - return {set, false}; - } - - VulkanResourceAllocator* mAllocator; - - // We need to heap-allocate so that the destruction can be strictly ordered. - std::unique_ptr mDescriptorPool; - std::unique_ptr> mUBOCache; - std::unique_ptr> mSamplerCache; - std::unique_ptr> mInputAttachmentCache; -}; - -} // anonymous namespace - -class VulkanDescriptorSetManager::Impl { -private: - using GetPipelineLayoutFunction = VulkanDescriptorSetManager::GetPipelineLayoutFunction; - using DescriptorSetVkHandles = utils::FixedCapacityVector; - - static inline DescriptorSetVkHandles initDescSetHandles() { - return DescriptorSetVkHandles::with_capacity( - VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT); - } - - struct BoundState { - BoundState() - : cmdbuf(VK_NULL_HANDLE), - pipelineLayout(VK_NULL_HANDLE), - vkSets(initDescSetHandles()) {} - - inline bool operator==(BoundState const& b) const { - if (cmdbuf != b.cmdbuf || pipelineLayout != b.pipelineLayout) { + bool operator==(BoundInfo const& info) const { + if (pipelineLayout != info.pipelineLayout || setMask != info.setMask) { return false; } - for (size_t i = 0; i < vkSets.size(); ++i) { - if (vkSets[i] != b.vkSets[i]) { + for (uint8_t i = 0; i < UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { + if ((bool) (setMask & (1LL << i)) && boundSets[i] != info.boundSets[i]) { return false; } } return true; } - - inline bool operator!=(BoundState const& b) const { - return !(*this == b); - } - - inline bool valid() noexcept { - return cmdbuf != VK_NULL_HANDLE; - } - - VkCommandBuffer cmdbuf; - VkPipelineLayout pipelineLayout; - DescriptorSetVkHandles vkSets; - VulkanDescriptorSetLayoutList layouts; }; - static constexpr uint8_t UBO_SET_ID = 0; - static constexpr uint8_t SAMPLER_SET_ID = 1; - static constexpr uint8_t INPUT_ATTACHMENT_SET_ID = 2; - public: - Impl(VkDevice device, VulkanResourceAllocator* allocator) + Impl(VkDevice device, VulkanResourceAllocator* resourceAllocator) : mDevice(device), - mAllocator(allocator), - mLayoutCache(device, allocator), - mDescriptorSetCache(device, allocator), - mHaveDynamicUbos(false), - mResources(allocator) {} - - VkPipelineLayout bind(VulkanCommandBuffer* commands, VulkanProgram* program, - GetPipelineLayoutFunction& getPipelineLayoutFn) { - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("bind"); - - VulkanDescriptorSetLayoutList layouts; - if (auto itr = mLayoutStash.find(program); itr != mLayoutStash.end()) { - layouts = itr->second; - } else { - auto const& layoutDescriptions = program->getLayoutDescriptionList(); - uint8_t count = 0; - for (auto const& description: layoutDescriptions) { - layouts[count++] = createLayout(description); - } - mLayoutStash[program] = layouts; - } - - VulkanDescriptorSetLayoutList outLayouts = layouts; - DescriptorSetVkHandles vkDescSets = initDescSetHandles(); - VkWriteDescriptorSet descriptorWrites[MAX_BINDINGS]; - uint32_t nwrites = 0; - - // Use placeholders when necessary - for (uint8_t i = 0; i < VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { - if (!layouts[i]) { - if (i == INPUT_ATTACHMENT_SET_ID || - (i == SAMPLER_SET_ID && !layouts[INPUT_ATTACHMENT_SET_ID])) { - continue; - } - outLayouts[i] = getPlaceHolderLayout(i); - } else { - outLayouts[i] = layouts[i]; - auto p = mAllocator->handle_cast(layouts[i]); - if (!((i == UBO_SET_ID && p->bitmask.ubo) - || (i == SAMPLER_SET_ID && p->bitmask.sampler) - || (i == INPUT_ATTACHMENT_SET_ID && p->bitmask.inputAttachment - && mInputAttachment.first.texture))) { - outLayouts[i] = getPlaceHolderLayout(i); - } - } - } - - for (uint8_t i = 0; i < VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { - if (!outLayouts[i]) { - continue; - } - VulkanDescriptorSetLayout* layout - = mAllocator->handle_cast(outLayouts[i]); - bool const usePlaceholder = layouts[i] != outLayouts[i]; - - auto const& [set, cached] = getSet(i, layout); - VkDescriptorSet const vkSet = set->vkSet; - commands->acquire(set); - vkDescSets.push_back(vkSet); - - // Note that we still need to bind the set, but 'cached' means that we found a set with - // the exact same content already written, and we would just bind that one instead. - // We also don't need to write to the placeholder set. - if (cached || usePlaceholder) { - continue; - } + mResourceAllocator(resourceAllocator), + mDescriptorPool(device) {} - switch (i) { - case UBO_SET_ID: { - for (uint8_t binding: layout->bindings.ubo) { - auto const& [info, ubo] = mUboMap[binding]; - descriptorWrites[nwrites++] = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = vkSet, - .dstBinding = binding, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = ubo ? &info : &mPlaceHolderBufferInfo, - }; - if (ubo) { - set->resources.acquire(ubo); - } - } - break; - } - case SAMPLER_SET_ID: { - for (uint8_t binding: layout->bindings.sampler) { - auto const& [info, texture] = mSamplerMap[binding]; - descriptorWrites[nwrites++] = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = vkSet, - .dstBinding = binding, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = texture ? &info : &mPlaceHolderImageInfo, - }; - if (texture) { - set->resources.acquire(texture); - } - } - break; - } - case INPUT_ATTACHMENT_SET_ID: { - descriptorWrites[nwrites++] = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = vkSet, - .dstBinding = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, - .pImageInfo = &mInputAttachment.second, - }; - set->resources.acquire(mInputAttachment.first.texture); - break; - } - default: - PANIC_POSTCONDITION("Invalid set id=%d", i); - } - } + // bind() is not really binding the set but just stashing until we have all the info + // (pipelinelayout). + void bind(uint8_t setIndex, VulkanDescriptorSet* set, + backend::DescriptorSetOffsetArray&& offsets) { + auto& history = mHistory[set->vkSet]; + history.setOffsets(std::move(offsets)); - if (nwrites) { - vkUpdateDescriptorSets(mDevice, nwrites, descriptorWrites, 0, nullptr); + auto const lastSet = mStashedSets[setIndex]; + if (lastSet != VK_NULL_HANDLE) { + assert_invariant(mHistory.find(lastSet) != mHistory.end()); + mHistory[lastSet].unbind(); } + mStashedSets[setIndex] = set->vkSet; + } - VkPipelineLayout const pipelineLayout = getPipelineLayoutFn(outLayouts, program); - VkCommandBuffer const cmdbuffer = commands->buffer(); - - BoundState state{}; - state.cmdbuf = cmdbuffer; - state.pipelineLayout = pipelineLayout; - state.vkSets = vkDescSets; - state.layouts = layouts; - - if (state != mBoundState) { - vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, - vkDescSets.size(), vkDescSets.data(), 0, nullptr); - mBoundState = state; - } + void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, uint8_t setMask) { + using iter_type = decltype(mHistory)::iterator; + std::array iterators = {mHistory.end()}; + bool allBound = true; - // Once bound, the resources are now ref'd in the descriptor set and the some resources can - // be released and the descriptor set is ref'd by the command buffer. - for (uint8_t i = 0; i < mSamplerMap.size(); ++i) { - auto const& [info, texture] = mSamplerMap[i]; - if (texture) { - mResources.release(texture); + for (uint8_t i = 0; i < UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { + if ((setMask & (1LL << i)) == 0) { + continue; + } + auto const vkset = mStashedSets[i]; + if (auto itr = mHistory.find(vkset); itr != mHistory.end() && !itr->second.bound()) { + allBound = false; + iterators[i] = itr; } - mSamplerMap[i] = {{}, nullptr}; } - mInputAttachment = {}; - mHaveDynamicUbos = false; - - FVK_SYSTRACE_END(); - return pipelineLayout; - } - - void dynamicBind(VulkanCommandBuffer* commands, Handle uboLayout) { - if (!mHaveDynamicUbos) { + BoundInfo nextInfo = { + pipelineLayout, + setMask, + mStashedSets, + }; + if (allBound && mLastBoundInfo == nextInfo) { return; } - FVK_SYSTRACE_CONTEXT(); - FVK_SYSTRACE_START("dynamic-bind"); - - assert_invariant(mBoundState.valid()); - assert_invariant(commands->buffer() == mBoundState.cmdbuf); - - auto layout = mAllocator->handle_cast( - mBoundState.layouts[UBO_SET_ID]); - - // Note that this is costly, instead just use dynamic UBOs with dynamic offsets. - auto const& [set, cached] = getSet(UBO_SET_ID, layout); - VkDescriptorSet const vkSet = set->vkSet; - - if (!cached) { - VkWriteDescriptorSet descriptorWrites[MAX_UBO_BINDING]; - uint8_t nwrites = 0; - - for (uint8_t binding: layout->bindings.ubo) { - auto const& [info, ubo] = mUboMap[binding]; - descriptorWrites[nwrites++] = { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = vkSet, - .dstBinding = binding, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pBufferInfo = ubo ? &info : &mPlaceHolderBufferInfo, - }; - if (ubo) { - set->resources.acquire(ubo); - } - } - if (nwrites > 0) { - vkUpdateDescriptorSets(mDevice, nwrites, descriptorWrites, 0, nullptr); - } - } - commands->acquire(set); - if (mBoundState.vkSets[UBO_SET_ID] != vkSet) { - vkCmdBindDescriptorSets(mBoundState.cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, - mBoundState.pipelineLayout, 0, 1, &vkSet, 0, nullptr); - mBoundState.vkSets[UBO_SET_ID] = vkSet; + for (uint8_t i = 0; i < UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { + auto& itr = iterators[i]; + if (itr != mHistory.end()) { + itr->second.bind(commands, pipelineLayout, i); + } } - mHaveDynamicUbos = false; - FVK_SYSTRACE_END(); - } - - void clearProgram(VulkanProgram* program) noexcept { - mLayoutStash.erase(program); - } - - Handle createLayout( - descset::DescriptorSetLayout const& description) { - return mLayoutCache.getLayout(description); + mLastBoundInfo = nextInfo; } - void destroyLayout(Handle layout) { - mLayoutCache.destroyLayout(layout); - } - - // Note that before [GDSR] arrives, the "update" methods stash state within this class and is - // not actually working with respect to a descriptor set. - void updateBuffer(Handle, uint8_t binding, - VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept { - VkDescriptorBufferInfo const info{ - .buffer = bufferObject->buffer.getGpuBuffer(), - .offset = offset, - .range = size, + void updateBuffer(VulkanDescriptorSet* set, uint8_t binding, VulkanBufferObject* bufferObject, + VkDeviceSize offset, VkDeviceSize size) noexcept { + VkDescriptorBufferInfo const info = { + .buffer = bufferObject->buffer.getGpuBuffer(), + .offset = offset, + .range = size, }; - mUboMap[binding] = {info, bufferObject}; - mResources.acquire(bufferObject); - if (!mHaveDynamicUbos && mBoundState.valid()) { - mHaveDynamicUbos = true; + VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + auto& history = mHistory[set->vkSet]; + + if (history.mask().dynamicUbo & (1LL << binding)) { + type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; } + VkWriteDescriptorSet const descriptorWrite = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = set->vkSet, + .dstBinding = binding, + .descriptorCount = 1, + .descriptorType = type, + .pBufferInfo = &info, + }; + vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr); + set->acquire(bufferObject); + history.write(binding); } - void updateSampler(Handle, uint8_t binding, VulkanTexture* texture, + void updateSampler(VulkanDescriptorSet* set, uint8_t binding, VulkanTexture* texture, VkSampler sampler) noexcept { VkDescriptorImageInfo info{ - .sampler = sampler, + .sampler = sampler, }; VkImageSubresourceRange const range = texture->getPrimaryViewRange(); VkImageViewType const expectedType = texture->getViewType(); - if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) - && expectedType == VK_IMAGE_VIEW_TYPE_2D) { + if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) && + expectedType == VK_IMAGE_VIEW_TYPE_2D) { // If the sampler is part of a mipmapped depth texture, where one of the level *can* be // an attachment, then the sampler for this texture has the same view properties as a // view for an attachment. Therefore, we can use getAttachmentView to get a @@ -1009,25 +457,22 @@ class VulkanDescriptorSetManager::Impl { info.imageView = texture->getViewForType(range, expectedType); } info.imageLayout = imgutil::getVkLayout(texture->getPrimaryImageLayout()); - mSamplerMap[binding] = {info, texture}; - mResources.acquire(texture); - } - - void updateInputAttachment(Handle, VulkanAttachment attachment) noexcept { - VkDescriptorImageInfo info = { - .imageView = attachment.getImageView(), - .imageLayout = imgutil::getVkLayout(attachment.getLayout()), + VkWriteDescriptorSet const descriptorWrite = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = set->vkSet, + .dstBinding = binding, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &info, }; - mInputAttachment = {attachment, info}; - mResources.acquire(attachment.texture); + vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr); + set->acquire(texture); + mHistory[set->vkSet].write(binding, range, texture); } - void clearBuffer(uint32_t binding) { - auto const& [info, ubo] = mUboMap[binding]; - if (ubo) { - mResources.release(ubo); - } - mUboMap[binding] = {{}, nullptr}; + void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept { + // TOOD: fill-in this region } void setPlaceHolders(VkSampler sampler, VulkanTexture* texture, @@ -1044,96 +489,44 @@ class VulkanDescriptorSetManager::Impl { }; } - void clearState() noexcept { - mHaveDynamicUbos = false; - if (mInputAttachment.first.texture) { - mResources.release(mInputAttachment.first.texture); - } - mInputAttachment = {}; - mBoundState = {}; - } - - inline void gc() { - mDescriptorSetCache.gc(); - } - -private: - inline std::pair getSet(uint8_t const setIndex, - VulkanDescriptorSetLayout* layout) { - switch (setIndex) { - case UBO_SET_ID: { - auto key = UBOKey::key(mUboMap, layout); - return mDescriptorSetCache.get(key, layout); - } - case SAMPLER_SET_ID: { - auto key = SamplerKey::key(mSamplerMap, layout); - return mDescriptorSetCache.get(key, layout); - } - case INPUT_ATTACHMENT_SET_ID: { - auto key = InputAttachmentKey::key(mInputAttachment.second, layout); - return mDescriptorSetCache.get(key, layout); + void createSet(Handle handle, VulkanDescriptorSetLayout* layout) { + auto const vkSet = mDescriptorPool.obtainSet(layout); + auto const& count = layout->count; + auto const& layoutMask = layout->bitmask; + auto set = mResourceAllocator->construct(handle, mResourceAllocator, + vkSet, [vkSet, count, layoutMask, this]() { + mHistory.erase(vkSet); + mDescriptorPool.recycle(count, layoutMask, vkSet); + }); + mHistory.emplace( + std::piecewise_construct, + std::forward_as_tuple(vkSet), + std::forward_as_tuple(layout->bitmask, layout->count, layout->vklayout, mResourceAllocator, set)); + } + + void destroySet(Handle handle) { + VulkanDescriptorSet* set = mResourceAllocator->handle_cast(handle); + mHistory.erase(set->vkSet); + for (uint8_t i = 0; i < mStashedSets.size(); ++i) { + if (mStashedSets[i] == set->vkSet) { + mStashedSets[i] = VK_NULL_HANDLE; } - default: - PANIC_POSTCONDITION("Invalid set-id=%d", setIndex); } } - inline Handle getPlaceHolderLayout(uint8_t setID) { - if (mPlaceholderLayout[setID]) { - return mPlaceholderLayout[setID]; - } - descset::DescriptorSetLayout inputLayout { - .bindings = {{}}, - }; - switch (setID) { - case UBO_SET_ID: - inputLayout.bindings[0] = { - .type = descset::DescriptorType::UNIFORM_BUFFER, - .stageFlags = descset::ShaderStageFlags2::VERTEX, - .binding = 0, - .flags = descset::DescriptorFlags::NONE, - .count = 0, - }; - break; - case SAMPLER_SET_ID: - inputLayout.bindings[0] = { - .type = descset::DescriptorType::SAMPLER, - .stageFlags = descset::ShaderStageFlags2::FRAGMENT, - .binding = 0, - .flags = descset::DescriptorFlags::NONE, - .count = 0, - }; - break; - case INPUT_ATTACHMENT_SET_ID: - inputLayout.bindings[0] = { - .type = descset::DescriptorType::INPUT_ATTACHMENT, - .stageFlags = descset::ShaderStageFlags2::FRAGMENT, - .binding = 0, - .flags = descset::DescriptorFlags::NONE, - .count = 0, - }; - break; - default: - PANIC_POSTCONDITION("Unexpected set id=%d", setID); - } - mPlaceholderLayout[setID] = mLayoutCache.getLayout(inputLayout); - return mPlaceholderLayout[setID]; - } +private: VkDevice mDevice; - VulkanResourceAllocator* mAllocator; - LayoutCache mLayoutCache; - DescriptorSetCache mDescriptorSetCache; - bool mHaveDynamicUbos; - UBOMap mUboMap; - SamplerMap mSamplerMap; + VulkanResourceAllocator* mResourceAllocator; + DescriptorInfinitePool mDescriptorPool; std::pair mInputAttachment; - VulkanResourceManager mResources; + std::unordered_map mHistory; + DescriptorSetArray mStashedSets = { VK_NULL_HANDLE }; + + BoundInfo mLastBoundInfo; + VkDescriptorBufferInfo mPlaceHolderBufferInfo; VkDescriptorImageInfo mPlaceHolderImageInfo; - std::unordered_map mLayoutStash; - BoundState mBoundState; - VulkanDescriptorSetLayoutList mPlaceholderLayout = {}; }; VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device, @@ -1146,59 +539,44 @@ void VulkanDescriptorSetManager::terminate() noexcept { mImpl = nullptr; } -void VulkanDescriptorSetManager::gc() { - mImpl->gc(); -} - -VkPipelineLayout VulkanDescriptorSetManager::bind(VulkanCommandBuffer* commands, - VulkanProgram* program, - VulkanDescriptorSetManager::GetPipelineLayoutFunction& getPipelineLayoutFn) { - return mImpl->bind(commands, program, getPipelineLayoutFn); -} - -void VulkanDescriptorSetManager::dynamicBind(VulkanCommandBuffer* commands, - Handle uboLayout) { - mImpl->dynamicBind(commands, uboLayout); -} - -void VulkanDescriptorSetManager::clearProgram(VulkanProgram* program) noexcept { - mImpl->clearProgram(program); +void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t binding, + VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept { + mImpl->updateBuffer(set, binding, bufferObject, offset, size); } -Handle VulkanDescriptorSetManager::createLayout( - descset::DescriptorSetLayout const& layout) { - return mImpl->createLayout(layout); +void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t binding, + VulkanTexture* texture, VkSampler sampler) noexcept { + mImpl->updateSampler(set, binding, texture, sampler); } -void VulkanDescriptorSetManager::destroyLayout(Handle layout) { - mImpl->destroyLayout(layout); +void VulkanDescriptorSetManager::updateInputAttachment(VulkanDescriptorSet* set, + VulkanAttachment attachment) noexcept { + mImpl->updateInputAttachment(set, attachment); } -void VulkanDescriptorSetManager::updateBuffer(Handle set, - uint8_t binding, VulkanBufferObject* bufferObject, VkDeviceSize offset, - VkDeviceSize size) noexcept { - mImpl->updateBuffer(set, binding, bufferObject, offset, size); +void VulkanDescriptorSetManager::setPlaceHolders(VkSampler sampler, VulkanTexture* texture, + VulkanBufferObject* bufferObject) noexcept { + mImpl->setPlaceHolders(sampler, texture, bufferObject); } -void VulkanDescriptorSetManager::updateSampler(Handle set, - uint8_t binding, VulkanTexture* texture, VkSampler sampler) noexcept { - mImpl->updateSampler(set, binding, texture, sampler); +void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set, + backend::DescriptorSetOffsetArray&& offsets) { + return mImpl->bind(setIndex, set, std::move(offsets)); } -void VulkanDescriptorSetManager::updateInputAttachment(Handle set, VulkanAttachment attachment) noexcept { - mImpl->updateInputAttachment(set, attachment); +void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands, + VkPipelineLayout pipelineLayout, uint8_t setMask) { + mImpl->commit(commands, pipelineLayout, setMask); } -void VulkanDescriptorSetManager::clearBuffer(uint32_t bindingIndex) { - mImpl->clearBuffer(bindingIndex); +void VulkanDescriptorSetManager::createSet(Handle handle, + VulkanDescriptorSetLayout* layout) { + mImpl->createSet(handle, layout); } -void VulkanDescriptorSetManager::setPlaceHolders(VkSampler sampler, VulkanTexture* texture, - VulkanBufferObject* bufferObject) noexcept { - mImpl->setPlaceHolders(sampler, texture, bufferObject); +void VulkanDescriptorSetManager::destroySet(Handle handle) { + mImpl->destroySet(handle); } -void VulkanDescriptorSetManager::clearState() noexcept { mImpl->clearState(); } - }// namespace filament::backend diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h index dbb969080aa..3d06ff2c8f6 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h @@ -30,8 +30,6 @@ #include #include -#include - namespace filament::backend { using namespace descset; @@ -45,60 +43,31 @@ class VulkanDescriptorSetManager { public: static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT; - using GetPipelineLayoutFunction = std::function; + + using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray; VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator); void terminate() noexcept; - void gc(); - - // TODO: Obsolete after [GDSR]. - // This will write/update/bind all of the descriptor set. After [GDSR], the binding for - // descriptor sets will not depend on the layout described in the program. - VkPipelineLayout bind(VulkanCommandBuffer* commands, VulkanProgram* program, - GetPipelineLayoutFunction& getPipelineLayoutFn); - - // TODO: Obsolete after [GDSR]. - // This is to "dynamically" bind UBOs that might have offsets changed between pipeline binding - // and the draw call. We do this because UBOs for primitives that are part of the same - // renderable can be stored within one buffer. This can be a no-op if there were no range - // changes between the pipeline bind and the draw call. We will re-use applicable states - // provided within the bind() call, including the UBO descriptor set layout. TODO: make it a - // proper dynamic binding when Filament-side descriptor changes are completed. - void dynamicBind(VulkanCommandBuffer* commands, Handle uboLayout); - - // TODO: Obsolete after [GDSR]. - // Since we use program pointer as cache key, we need to clear the cache when it's freed. - void clearProgram(VulkanProgram* program) noexcept; - - Handle createLayout(descset::DescriptorSetLayout const& layout); - - void destroyLayout(Handle layout); - - void updateBuffer(Handle set, uint8_t binding, + void updateBuffer(VulkanDescriptorSet* set, uint8_t binding, VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept; - void updateSampler(Handle set, uint8_t binding, + void updateSampler(VulkanDescriptorSet* set, uint8_t binding, VulkanTexture* texture, VkSampler sampler) noexcept; - void updateInputAttachment(Handle set, VulkanAttachment attachment) noexcept; + void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept; + + void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets); - void clearBuffer(uint32_t bindingIndex); + void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, uint8_t setMask); void setPlaceHolders(VkSampler sampler, VulkanTexture* texture, VulkanBufferObject* bufferObject) noexcept; - void clearState() noexcept; + void createSet(Handle handle, VulkanDescriptorSetLayout* layout); - // TODO: To be completed after [GDSR] - Handle createSet(Handle layout) { - return Handle(); - } - - // TODO: To be completed after [GDSR] - void destroySet(Handle set) {} + void destroySet(Handle handle); private: class Impl; @@ -108,4 +77,3 @@ class VulkanDescriptorSetManager { }// namespace filament::backend #endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H - diff --git a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp index 861e840bed2..55c2157b45b 100644 --- a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp +++ b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.cpp @@ -21,14 +21,15 @@ namespace filament::backend { VkPipelineLayout VulkanPipelineLayoutCache::getLayout( - VulkanDescriptorSetLayoutList const& descriptorSetLayouts, VulkanProgram* program) { + DescriptorSetLayoutArray const& descriptorSetLayouts, VulkanProgram* program) { PipelineLayoutKey key = {}; uint8_t descSetLayoutCount = 0; + key.descSetLayouts = descriptorSetLayouts; for (auto layoutHandle: descriptorSetLayouts) { - if (layoutHandle) { - auto layout = mAllocator->handle_cast(layoutHandle); - key.descSetLayouts[descSetLayoutCount++] = layout->vklayout; + if (layoutHandle == VK_NULL_HANDLE) { + break; } + descSetLayoutCount++; } // build the push constant layout key diff --git a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h index 42bd2926acd..4122b8e2197 100644 --- a/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h +++ b/filament/backend/src/vulkan/caching/VulkanPipelineLayoutCache.h @@ -28,9 +28,10 @@ namespace filament::backend { class VulkanPipelineLayoutCache { public: + using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray; + VulkanPipelineLayoutCache(VkDevice device, VulkanResourceAllocator* allocator) : mDevice(device), - mAllocator(allocator), mTimestamp(0) {} void terminate() noexcept; @@ -43,20 +44,18 @@ class VulkanPipelineLayoutCache { }; struct PipelineLayoutKey { - using DescriptorSetLayoutArray = std::array; - DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 3 - PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3 + DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 4 + PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3 uint16_t padding = 0; }; - static_assert(sizeof(PipelineLayoutKey) == 32); + static_assert(sizeof(PipelineLayoutKey) == 40); VulkanPipelineLayoutCache(VulkanPipelineLayoutCache const&) = delete; VulkanPipelineLayoutCache& operator=(VulkanPipelineLayoutCache const&) = delete; // A pipeline layout depends on the descriptor set layout and the push constant ranges, which // are described in the program. - VkPipelineLayout getLayout(VulkanDescriptorSetLayoutList const& descriptorSetLayouts, + VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts, VulkanProgram* program); private: @@ -77,7 +76,6 @@ class VulkanPipelineLayoutCache { PipelineLayoutKeyHashFn, PipelineLayoutKeyEqual>; VkDevice mDevice; - VulkanResourceAllocator* mAllocator; Timestamp mTimestamp; PipelineLayoutMap mPipelineLayouts; }; From 9a1ee1ff6725f8e1a2370f3a67af5119c0d4cfa4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 18 Jun 2024 14:11:04 -0700 Subject: [PATCH 04/49] fix bad merge --- filament/backend/src/opengl/OpenGLProgram.h | 1 + 1 file changed, 1 insertion(+) diff --git a/filament/backend/src/opengl/OpenGLProgram.h b/filament/backend/src/opengl/OpenGLProgram.h index 3b8afa3ccdc..a5d35122e9b 100644 --- a/filament/backend/src/opengl/OpenGLProgram.h +++ b/filament/backend/src/opengl/OpenGLProgram.h @@ -72,6 +72,7 @@ class OpenGLProgram : public HwProgram { } context.useProgram(gl.program); + return true; } GLuint getBufferBinding(descriptor_set_t set, descriptor_binding_t binding) const noexcept { From 2e763149e77dd51048ebbb652eec75270b229d41 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Wed, 29 May 2024 16:10:49 -0700 Subject: [PATCH 05/49] MetalArgumentBuffer support buffers --- filament/backend/src/metal/MetalDriver.mm | 12 ++++++++++++ filament/backend/src/metal/MetalHandles.h | 8 ++++++++ libs/filamat/src/MetalArgumentBuffer.cpp | 11 +++++++++++ libs/filamat/src/MetalArgumentBuffer.h | 18 +++++++++++++++++- libs/filamat/tests/test_argBufferFixup.cpp | 20 ++++++++++++++++++++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index caabe35834c..b360c7cf75f 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -295,6 +295,8 @@ backend::BufferObjectHandle boh, uint32_t offset, uint32_t size) { + ASSERT_PRECONDITION(!isInRenderPass(mContext), + "updateDescriptorSetBuffer must be called outside of a render pass."); } void MetalDriver::updateDescriptorSetTexture( @@ -302,6 +304,8 @@ backend::descriptor_binding_t binding, backend::TextureHandle th, SamplerParams params) { + ASSERT_PRECONDITION(!isInRenderPass(mContext), + "updateDescriptorSetTexture must be called outside of a render pass."); } void MetalDriver::flush(int) { @@ -487,10 +491,12 @@ void MetalDriver::createDescriptorSetLayoutR(Handle dslh, DescriptorSetLayout&& info) { + construct_handle(dslh, std::move(info)); } void MetalDriver::createDescriptorSetR(Handle dsh, Handle dslh) { + construct_handle(dsh, dslh); } Handle MetalDriver::createVertexBufferInfoS() noexcept { @@ -679,9 +685,15 @@ } void MetalDriver::destroyDescriptorSetLayout(Handle dslh) { + if (dslh) { + destruct_handle(dslh); + } } void MetalDriver::destroyDescriptorSet(Handle dsh) { + if (dsh) { + destruct_handle(dsh); + } } void MetalDriver::terminate() { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 4f7c0eb7b0f..f10f9f18ec7 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -547,10 +547,18 @@ struct MetalTimerQuery : public HwTimerQuery { std::shared_ptr status; }; +// Descriptor sets are layed out as follows: +// Buffers +// Textures + struct MetalDescriptorSetLayout : public HwDescriptorSetLayout { + MetalDescriptorSetLayout(DescriptorSetLayout&& layout) noexcept : layout(std::move(layout)) {} + DescriptorSetLayout layout; }; struct MetalDescriptorSet : public HwDescriptorSet { + MetalDescriptorSet(Handle layout) noexcept : layout(layout) {} + Handle layout; }; } // namespace backend diff --git a/libs/filamat/src/MetalArgumentBuffer.cpp b/libs/filamat/src/MetalArgumentBuffer.cpp index 506cb8e480a..af145c2812a 100644 --- a/libs/filamat/src/MetalArgumentBuffer.cpp +++ b/libs/filamat/src/MetalArgumentBuffer.cpp @@ -52,6 +52,12 @@ MetalArgumentBuffer::Builder& MetalArgumentBuffer::Builder::sampler( return *this; } +MetalArgumentBuffer::Builder& MetalArgumentBuffer::Builder::buffer( + size_t index, const std::string& type, const std::string& name) noexcept { + mArguments.emplace_back(BufferArgument { name, index, type }); + return *this; +} + MetalArgumentBuffer* MetalArgumentBuffer::Builder::build() { assert_invariant(!mName.empty()); return new MetalArgumentBuffer(*this); @@ -114,6 +120,11 @@ std::ostream& MetalArgumentBuffer::Builder::SamplerArgument::write(std::ostream& return os; } +std::ostream& MetalArgumentBuffer::Builder::BufferArgument::write(std::ostream& os) const { + os << "constant " << type << "* " << name << " [[id(" << index << ")]];" << std::endl; + return os; +} + MetalArgumentBuffer::MetalArgumentBuffer(Builder& builder) { mName = builder.mName; diff --git a/libs/filamat/src/MetalArgumentBuffer.h b/libs/filamat/src/MetalArgumentBuffer.h index 608645fca12..75f4d1a3fc3 100644 --- a/libs/filamat/src/MetalArgumentBuffer.h +++ b/libs/filamat/src/MetalArgumentBuffer.h @@ -58,6 +58,14 @@ class MetalArgumentBuffer { */ Builder& sampler(size_t index, const std::string& name) noexcept; + /** + * Add a buffer argument to the argument buffer structure. + * @param index the [[id(n)]] index of the buffer argument + * @param type the type of data the buffer points to + * @param name the name of the buffer argument + */ + Builder& buffer(size_t index, const std::string& type, const std::string& name) noexcept; + MetalArgumentBuffer* build(); friend class MetalArgumentBuffer; @@ -82,7 +90,15 @@ class MetalArgumentBuffer { std::ostream& write(std::ostream& os) const; }; - using ArgumentType = std::variant; + struct BufferArgument { + std::string name; + size_t index; + std::string type; + + std::ostream& write(std::ostream& os) const; + }; + + using ArgumentType = std::variant; std::vector mArguments; }; diff --git a/libs/filamat/tests/test_argBufferFixup.cpp b/libs/filamat/tests/test_argBufferFixup.cpp index 7a214bca34d..7e6d426f19f 100644 --- a/libs/filamat/tests/test_argBufferFixup.cpp +++ b/libs/filamat/tests/test_argBufferFixup.cpp @@ -88,6 +88,24 @@ TEST(ArgBufferFixup, TextureAndSampler) { MetalArgumentBuffer::destroy(&argBuffer); } +TEST(ArgBufferFixup, Buffer) { + auto argBuffer = + MetalArgumentBuffer::Builder() + .name("myArgumentBuffer") + .buffer(0, "FrameUniforms", "frameUniforms") + .build(); + auto argBufferStr = argBuffer->getMsl(); + + const std::string expected = + "struct myArgumentBuffer {\n" + "constant FrameUniforms* frameUniforms [[id(0)]];\n" + "}"; + + EXPECT_EQ(argBuffer->getMsl(), expected); + + MetalArgumentBuffer::destroy(&argBuffer); +} + TEST(ArgBufferFixup, TextureAndSamplerMS) { auto argBuffer = MetalArgumentBuffer::Builder() @@ -114,6 +132,7 @@ TEST(ArgBufferFixup, Sorted) { .name("myArgumentBuffer") .sampler(3, "samplerB") .texture(0, "textureA", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, false) + .buffer(4, "FrameUniforms", "frameUniforms") .texture(2, "textureB", SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, false) .sampler(1, "samplerA") .build(); @@ -125,6 +144,7 @@ TEST(ArgBufferFixup, Sorted) { "sampler samplerA [[id(1)]];\n" "texture2d textureB [[id(2)]];\n" "sampler samplerB [[id(3)]];\n" + "constant FrameUniforms* frameUniforms [[id(4)]];\n" "}"; EXPECT_EQ(argBuffer->getMsl(), expected); From e75882e4d4cef1afc9854cddcbcdaaf7580da723 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Fri, 31 May 2024 10:56:09 -0700 Subject: [PATCH 06/49] Update MetalArgumentBuffer to support buffers --- libs/filamat/src/MetalArgumentBuffer.cpp | 16 ++++++++++++--- libs/filamat/tests/test_argBufferFixup.cpp | 24 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/libs/filamat/src/MetalArgumentBuffer.cpp b/libs/filamat/src/MetalArgumentBuffer.cpp index af145c2812a..ffe1aa1568d 100644 --- a/libs/filamat/src/MetalArgumentBuffer.cpp +++ b/libs/filamat/src/MetalArgumentBuffer.cpp @@ -18,6 +18,7 @@ #include #include +#include namespace filamat { @@ -128,9 +129,6 @@ std::ostream& MetalArgumentBuffer::Builder::BufferArgument::write(std::ostream& MetalArgumentBuffer::MetalArgumentBuffer(Builder& builder) { mName = builder.mName; - std::stringstream ss; - ss << "struct " << mName << " {" << std::endl; - auto& args = builder.mArguments; // Sort the arguments by index. @@ -145,6 +143,18 @@ MetalArgumentBuffer::MetalArgumentBuffer(Builder& builder) { [](auto const& x, auto const& y) { return x.index == y.index; }, lhs, rhs); }) == args.end()); + std::stringstream ss; + + // Add forward declarations of buffers. + for (const auto& a : builder.mArguments) { + if (std::holds_alternative(a)) { + const auto& bufferArg = std::get(a); + ss << "struct " << bufferArg.type << ";" << std::endl; + } + } + + ss << "struct " << mName << " {" << std::endl; + for (const auto& a : builder.mArguments) { std::visit([&](auto&& arg) { arg.write(ss); diff --git a/libs/filamat/tests/test_argBufferFixup.cpp b/libs/filamat/tests/test_argBufferFixup.cpp index 7e6d426f19f..549b5b07354 100644 --- a/libs/filamat/tests/test_argBufferFixup.cpp +++ b/libs/filamat/tests/test_argBufferFixup.cpp @@ -97,6 +97,7 @@ TEST(ArgBufferFixup, Buffer) { auto argBufferStr = argBuffer->getMsl(); const std::string expected = + "struct FrameUniforms;\n" "struct myArgumentBuffer {\n" "constant FrameUniforms* frameUniforms [[id(0)]];\n" "}"; @@ -106,6 +107,28 @@ TEST(ArgBufferFixup, Buffer) { MetalArgumentBuffer::destroy(&argBuffer); } +TEST(ArgBufferFixup, MultipleBuffers) { + auto argBuffer = + MetalArgumentBuffer::Builder() + .name("myArgumentBuffer") + .buffer(0, "FrameUniforms", "frameUniforms") + .buffer(1, "ObjectUniforms", "objectUniforms") + .build(); + auto argBufferStr = argBuffer->getMsl(); + + const std::string expected = + "struct FrameUniforms;\n" + "struct ObjectUniforms;\n" + "struct myArgumentBuffer {\n" + "constant FrameUniforms* frameUniforms [[id(0)]];\n" + "constant ObjectUniforms* objectUniforms [[id(1)]];\n" + "}"; + + EXPECT_EQ(argBuffer->getMsl(), expected); + + MetalArgumentBuffer::destroy(&argBuffer); +} + TEST(ArgBufferFixup, TextureAndSamplerMS) { auto argBuffer = MetalArgumentBuffer::Builder() @@ -139,6 +162,7 @@ TEST(ArgBufferFixup, Sorted) { auto argBufferStr = argBuffer->getMsl(); const std::string expected = + "struct FrameUniforms;\n" "struct myArgumentBuffer {\n" "texture2d textureA [[id(0)]];\n" "sampler samplerA [[id(1)]];\n" From 1612cdb353a7db17b8d661a54da0182ea1f72feb Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 3 Jun 2024 12:37:30 -0700 Subject: [PATCH 07/49] WIP: Implement Metal descriptor sets --- filament/backend/CMakeLists.txt | 17 + .../backend/include/backend/DriverEnums.h | 29 ++ filament/backend/src/metal/MetalBuffer.h | 2 +- filament/backend/src/metal/MetalBuffer.mm | 9 +- filament/backend/src/metal/MetalContext.h | 55 +++ filament/backend/src/metal/MetalContext.mm | 2 +- filament/backend/src/metal/MetalDriver.h | 1 + filament/backend/src/metal/MetalDriver.mm | 169 +++++++- filament/backend/src/metal/MetalHandles.h | 176 +++++++- filament/backend/src/metal/MetalHandles.mm | 75 ++++ filament/backend/src/metal/MetalState.h | 46 +- filament/backend/src/metal/MetalState.mm | 67 ++- filament/backend/test/MetalTest.mm | 159 +++++++ filament/backend/test/ShaderGenerator.cpp | 22 +- filament/backend/test/ShaderGenerator.h | 5 +- filament/backend/test/mac_runner.mm | 3 +- filament/backend/test/test_Blit.cpp | 16 +- filament/backend/test/test_BufferUpdates.cpp | 18 +- filament/backend/test/test_FeedbackLoops.cpp | 25 +- filament/backend/test/test_LoadImage.cpp | 76 ++-- filament/backend/test/test_MipLevels.cpp | 14 +- .../backend/test/test_RenderExternalImage.cpp | 38 +- filament/src/DFG.cpp | 2 + filament/src/details/Texture.cpp | 2 + libs/filabridge/src/DescriptorSets.cpp | 1 + libs/filamat/src/GLSLPostProcessor.cpp | 399 ++++++++++++++++-- libs/filamat/src/GLSLPostProcessor.h | 16 +- libs/filamat/src/MaterialBuilder.cpp | 2 + libs/utils/include/utils/bitset.h | 4 + 29 files changed, 1273 insertions(+), 177 deletions(-) create mode 100644 filament/backend/test/MetalTest.mm diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 79d8ce967b7..078953915d3 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -518,3 +518,20 @@ target_link_libraries(compute_test PRIVATE set_target_properties(compute_test PROPERTIES FOLDER Tests) endif() + +# ================================================================================================== +# Metal utils tests + +if (APPLE AND NOT IOS) + +add_executable(metal_utils_test test/MetalTest.mm) + +target_link_libraries(metal_utils_test PRIVATE + backend + getopt + gtest + ) + +set_target_properties(metal_utils_test PROPERTIES FOLDER Tests) + +endif() diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index c15cbedcbb1..6ccfe3de48b 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -419,6 +419,18 @@ enum class SamplerType : uint8_t { SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2) }; +inline const char* stringify(SamplerType samplerType) { + switch (samplerType) { + case SamplerType::SAMPLER_2D: return "SAMPLER_2D"; + case SamplerType::SAMPLER_2D_ARRAY: return "SAMPLER_2D_ARRAY"; + case SamplerType::SAMPLER_CUBEMAP: return "SAMPLER_CUBEMAP"; + case SamplerType::SAMPLER_EXTERNAL: return "SAMPLER_EXTERNAL"; + case SamplerType::SAMPLER_3D: return "SAMPLER_3D"; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: return "SAMPLER_CUBEMAP_ARRAY"; + } + return "UNKNOWN"; +} + //! Subpass type enum class SubpassType : uint8_t { SUBPASS_INPUT @@ -747,6 +759,23 @@ enum class TextureUsage : uint16_t { DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage }; +inline const char* stringify(TextureUsage usage) { + switch (usage) { + case TextureUsage::NONE: return "NONE"; + case TextureUsage::COLOR_ATTACHMENT: return "COLOR_ATTACHMENT"; + case TextureUsage::DEPTH_ATTACHMENT: return "DEPTH_ATTACHMENT"; + case TextureUsage::STENCIL_ATTACHMENT: return "STENCIL_ATTACHMENT"; + case TextureUsage::UPLOADABLE: return "UPLOADABLE"; + case TextureUsage::SAMPLEABLE: return "SAMPLEABLE"; + case TextureUsage::SUBPASS_INPUT: return "SUBPASS_INPUT"; + case TextureUsage::BLIT_SRC: return "BLIT_SRC"; + case TextureUsage::BLIT_DST: return "BLIT_DST"; + case TextureUsage::PROTECTED: return "PROTECTED"; + case TextureUsage::DEFAULT: return "DEFAULT"; + default: return "UNKNOWN"; + } +} + //! Texture swizzle enum class TextureSwizzle : uint8_t { SUBSTITUTE_ZERO, diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h index 081ca6c3fd0..ab95d79a81d 100644 --- a/filament/backend/src/metal/MetalBuffer.h +++ b/filament/backend/src/metal/MetalBuffer.h @@ -180,7 +180,7 @@ class MetalBuffer { * is no device allocation. * */ - id getGpuBufferForDraw(id cmdBuffer) noexcept; + id getGpuBufferForDraw() noexcept; void* getCpuBuffer() const noexcept { return mCpuBuffer; } diff --git a/filament/backend/src/metal/MetalBuffer.mm b/filament/backend/src/metal/MetalBuffer.mm index 83fe25d5983..f916571f6d1 100644 --- a/filament/backend/src/metal/MetalBuffer.mm +++ b/filament/backend/src/metal/MetalBuffer.mm @@ -31,12 +31,15 @@ // If the buffer is less than 4K in size and is updated frequently, we don't use an explicit // buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:. // This won't work for SSBOs, since they are read/write. + + /* if (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE && usage == BufferUsage::DYNAMIC && !forceGpuBuffer) { mBuffer = nil; mCpuBuffer = malloc(size); return; } + */ // Otherwise, we allocate a private GPU buffer. { @@ -96,7 +99,7 @@ copyIntoBuffer(src, size, byteOffset); } -id MetalBuffer::getGpuBufferForDraw(id cmdBuffer) noexcept { +id MetalBuffer::getGpuBufferForDraw() noexcept { // If there's a CPU buffer, then we return nil here, as the CPU-side buffer will be bound // separately. if (mCpuBuffer) { @@ -139,7 +142,7 @@ } // getGpuBufferForDraw() might return nil, which means there isn't a device allocation for // this buffer. In this case, we'll bind the buffer below with the CPU-side memory. - id gpuBuffer = buffer->getGpuBufferForDraw(cmdBuffer); + id gpuBuffer = buffer->getGpuBufferForDraw(); if (!gpuBuffer) { continue; } @@ -199,5 +202,5 @@ } } -} // namespace backend +} // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 8a14d9a36c8..faf59de8815 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -21,6 +21,8 @@ #include "MetalShaderCompiler.h" #include "MetalState.h" +#include + #include #include #include @@ -52,6 +54,7 @@ class MetalTimerQueryInterface; struct MetalUniformBuffer; struct MetalIndexBuffer; struct MetalVertexBuffer; +struct MetalDescriptorSet; constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples @@ -67,6 +70,53 @@ class MetalPushConstantBuffer { bool mDirty = false; }; +class MetalDynamicOffsets { +public: + void setOffsets(uint32_t set, const uint32_t* offsets, uint32_t count) { + assert(set < MAX_DESCRIPTOR_SET_COUNT); + + auto getStartIndexForSet = [&](uint32_t s) { + uint32_t startIndex = 0; + for (uint32_t i = 0; i < s; i++) { + startIndex += mCounts[i]; + } + return startIndex; + }; + + const bool resizeNecessary = mCounts[set] != count; + if (UTILS_UNLIKELY(resizeNecessary)) { + int delta = count - mCounts[set]; + + auto thisSetStart = mOffsets.begin() + getStartIndexForSet(set); + if (delta > 0) { + mOffsets.insert(thisSetStart, delta, 0); + } else { + mOffsets.erase(thisSetStart, thisSetStart - delta); + } + + mCounts[set] = count; + } + + if (resizeNecessary || + !std::equal( + offsets, offsets + count, mOffsets.begin() + getStartIndexForSet(set))) { + std::copy(offsets, offsets + count, mOffsets.begin() + getStartIndexForSet(set)); + mDirty = true; + } + } + bool isDirty() const { return mDirty; } + void setDirty(bool dirty) { mDirty = dirty; } + + std::pair getOffsets() const { + return { mOffsets.size(), mOffsets.data() }; + } + +private: + std::array mCounts = { 0 }; + std::vector mOffsets; + bool mDirty = false; +}; + struct MetalContext { explicit MetalContext(size_t metalFreedTextureListSize) : texturesToDestroy(metalFreedTextureListSize) {} @@ -127,6 +177,10 @@ struct MetalContext { // Keeps track of sampler groups we've finalized for the current render pass. tsl::robin_set finalizedSamplerGroups; + tsl::robin_set finalizedDescriptorSets; + std::array currentDescriptorSets = {}; + MetalBufferBindings descriptorBindings; + MetalDynamicOffsets dynamicOffsets; // Keeps track of all alive sampler groups, textures. tsl::robin_set samplerGroups; @@ -151,6 +205,7 @@ struct MetalContext { // Empty texture used to prevent GPU errors when a sampler has been bound without a texture. id emptyTexture = nil; + id emptyBuffer = nil; MetalBlitter* blitter = nullptr; diff --git a/filament/backend/src/metal/MetalContext.mm b/filament/backend/src/metal/MetalContext.mm index 6eb7518d31c..077a999b715 100644 --- a/filament/backend/src/metal/MetalContext.mm +++ b/filament/backend/src/metal/MetalContext.mm @@ -167,7 +167,7 @@ bool isInRenderPass(MetalContext* context) { void MetalPushConstantBuffer::setBytes(id encoder, ShaderStage stage) { constexpr size_t PUSH_CONSTANT_SIZE_BYTES = 4; - constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 26; + constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 20; static char buffer[MAX_PUSH_CONSTANT_COUNT * PUSH_CONSTANT_SIZE_BYTES]; assert_invariant(mPushConstants.size() <= MAX_PUSH_CONSTANT_COUNT); diff --git a/filament/backend/src/metal/MetalDriver.h b/filament/backend/src/metal/MetalDriver.h index e38b65b3755..5185046c62a 100644 --- a/filament/backend/src/metal/MetalDriver.h +++ b/filament/backend/src/metal/MetalDriver.h @@ -62,6 +62,7 @@ class MetalDriver final : public DriverBase { private: friend class MetalSwapChain; + friend struct MetalDescriptorSet; MetalPlatform& mPlatform; MetalContext* mContext; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index b360c7cf75f..5c19cd8e518 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -112,6 +112,9 @@ mContext->device = mPlatform.createDevice(); assert_invariant(mContext->device); + mContext->emptyBuffer = [mContext->device newBufferWithLength:4 * 1024 + options:MTLResourceStorageModePrivate]; + initializeSupportedGpuFamilies(mContext); utils::slog.v << "Supported GPU families: " << utils::io::endl; @@ -221,6 +224,8 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, int64_t refreshIntervalNs, uint32_t frameId) { + printf("[DS] beginFrame(monotonic_clock_ns = %lld, refreshIntervalNs = %lld, frameId = %d)\n", + monotonic_clock_ns, refreshIntervalNs, frameId); #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId); #endif @@ -257,6 +262,7 @@ } void MetalDriver::endFrame(uint32_t frameId) { + printf("[DS] endFrame(frameId = %d)\n", frameId); // If we haven't committed the command buffer (if the frame was canceled), do it now. There may // be commands in it (like fence signaling) that need to execute. submitPendingCommands(mContext); @@ -297,6 +303,12 @@ uint32_t size) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "updateDescriptorSetBuffer must be called outside of a render pass."); + printf("[DS] updateDescriptorSetBuffer(dsh = %d, binding = %d, boh = %d, offset = %d, size = " + "%d)\n", + dsh.getId(), binding, boh.getId(), offset, size); + + auto* descriptorSet = handle_cast(dsh); + descriptorSet->buffers[binding] = { boh, offset, size }; } void MetalDriver::updateDescriptorSetTexture( @@ -306,6 +318,12 @@ SamplerParams params) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "updateDescriptorSetTexture must be called outside of a render pass."); + printf("[DS] updateDescriptorSetTexture(dsh = %d, binding = %d, th = %d, params = {...})\n", + dsh.getId(), binding, th.getId()); + + + auto* descriptorSet = handle_cast(dsh); + descriptorSet->textures[binding] = MetalDescriptorSet::TextureBinding{ th, params }; } void MetalDriver::flush(int) { @@ -358,6 +376,10 @@ target, levels, format, samples, width, height, depth, usage, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3)); + + printf("createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = " + "%d, height = %d, depth = %d, usage = %s)\n", + th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); } void MetalDriver::createTextureSwizzledR(Handle th, SamplerType target, uint8_t levels, @@ -370,6 +392,10 @@ mContext->textures.insert(construct_handle(th, *mContext, target, levels, format, samples, width, height, depth, usage, r, g, b, a)); + + printf("createTextureSwizzledR(th = %d, target = %s, levels = %d, format = ?, samples = %d, " + "width = %d, height = %d, depth = %d, usage = %s, r = ?, g = ?, b = ?, a = ?)\n", + th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); } void MetalDriver::importTextureR(Handle th, intptr_t i, @@ -489,14 +515,68 @@ // nothing to do, timer query was constructed in createTimerQueryS } +const char* prettyDescriptorType(DescriptorType type) { + switch (type) { + case DescriptorType::UNIFORM_BUFFER: return "UNIFORM_BUFFER"; + case DescriptorType::SHADER_STORAGE_BUFFER: return "SHADER_STORAGE_BUFFER"; + case DescriptorType::SAMPLER: return "SAMPLER"; + case DescriptorType::INPUT_ATTACHMENT: return "INPUT_ATTACHMENT"; + } +} + +const char* prettyShaderStageFlags(ShaderStageFlags flags) { + std::vector stages; + if (any(flags & ShaderStageFlags::VERTEX)) { + stages.push_back("VERTEX"); + } + if (any(flags & ShaderStageFlags::FRAGMENT)) { + stages.push_back("FRAGMENT"); + } + if (any(flags & ShaderStageFlags::COMPUTE)) { + stages.push_back("COMPUTE"); + } + if (stages.empty()) { + return "NONE"; + } + static char buffer[64]; + buffer[0] = '\0'; + for (size_t i = 0; i < stages.size(); i++) { + if (i > 0) { + strcat(buffer, " | "); + } + strcat(buffer, stages[i]); + } + return buffer; +} + +const char* prettyDescriptorFlags(DescriptorFlags flags) { + if (flags == DescriptorFlags::DYNAMIC_OFFSET) { + return "DYNAMIC_OFFSET"; + } + return "NONE"; +} + void MetalDriver::createDescriptorSetLayoutR(Handle dslh, DescriptorSetLayout&& info) { + printf("[DS] createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); + for (size_t i = 0; i < info.bindings.size(); i++) { + printf(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s}", + info.bindings[i].binding, prettyDescriptorType(info.bindings[i].type), + info.bindings[i].count, prettyShaderStageFlags(info.bindings[i].stageFlags), + prettyDescriptorFlags(info.bindings[i].flags)); + printf(",\n"); + } + printf("})\n"); + std::sort(info.bindings.begin(), info.bindings.end(), + [](const auto& a, const auto& b) { return a.binding < b.binding; }); construct_handle(dslh, std::move(info)); } void MetalDriver::createDescriptorSetR(Handle dsh, Handle dslh) { - construct_handle(dsh, dslh); + printf("[DS] createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId()); + MetalDescriptorSetLayout* layout = handle_cast(dslh); + construct_handle(dsh, layout); } Handle MetalDriver::createVertexBufferInfoS() noexcept { @@ -568,11 +648,11 @@ } Handle MetalDriver::createDescriptorSetLayoutS() noexcept { - return alloc_and_construct_handle(); + return alloc_handle(); } Handle MetalDriver::createDescriptorSetS() noexcept { - return alloc_and_construct_handle(); + return alloc_handle(); } void MetalDriver::destroyVertexBufferInfo(Handle vbih) { @@ -685,12 +765,14 @@ } void MetalDriver::destroyDescriptorSetLayout(Handle dslh) { + printf("[DS] destroyDescriptorSetLayout(dslh = %d)\n", dslh.getId()); if (dslh) { destruct_handle(dslh); } } void MetalDriver::destroyDescriptorSet(Handle dsh) { + printf("[DS] destroyDescriptorSet(dsh = %d)\n", dsh.getId()); if (dsh) { destruct_handle(dsh); } @@ -965,6 +1047,9 @@ void MetalDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { auto tex = handle_cast(th); tex->setLodRange(minLevel, maxLevel); + + printf("setMinMaxLevels(th = %d, minLevel = %d, maxLevel = %d)\n", th.getId(), minLevel, + maxLevel); } void MetalDriver::update3DImage(Handle th, uint32_t level, @@ -976,6 +1061,10 @@ auto tex = handle_cast(th); tex->loadImage(level, MTLRegionMake3D(xoffset, yoffset, zoffset, width, height, depth), data); scheduleDestroy(std::move(data)); + + printf("update3DImage(th = %d, level = %d, xoffset = %d, yoffset = %d, zoffset = %d, width = " + "%d, height = %d, depth = %d, data = ?)\n", + th.getId(), level, xoffset, yoffset, zoffset, width, height, depth); } void MetalDriver::setupExternalImage(void* image) { @@ -1015,6 +1104,8 @@ << "generateMipmaps must be called outside of a render pass."; auto tex = handle_cast(th); tex->generateMipmaps(); + + printf("generateMipmaps(th = %d)\n", th.getId()); } void MetalDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { @@ -1068,7 +1159,7 @@ }); auto& encoderCache = mContext->argumentEncoderCache; id encoder = - encoderCache.getOrCreateState(ArgumentEncoderState(std::move(textureTypes))); + encoderCache.getOrCreateState(ArgumentEncoderState(0, std::move(textureTypes))); sb->reset(getPendingCommandBuffer(mContext), encoder, mContext->device); // In a perfect world, all the MTLTexture bindings would be known at updateSamplerGroup time. @@ -1116,6 +1207,7 @@ void MetalDriver::beginRenderPass(Handle rth, const RenderPassParams& params) { + printf("[DS] beginRenderPass(rth = %d, params = {...})\n", rth.getId()); #if defined(FILAMENT_METAL_PROFILING) const char* renderPassName = "Unknown"; @@ -1158,6 +1250,9 @@ mContext->currentPolygonOffset = {0.0f, 0.0f}; mContext->finalizedSamplerGroups.clear(); + mContext->finalizedDescriptorSets.clear(); + mContext->descriptorBindings.invalidate(); + mContext->dynamicOffsets.setDirty(true); for (auto& pc : mContext->currentPushConstants) { pc.clear(); @@ -1167,6 +1262,7 @@ void MetalDriver::nextSubpass(int dummy) {} void MetalDriver::endRenderPass(int dummy) { + printf("[DS] endRenderPass()\n"); #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_end(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass"); #endif @@ -1514,6 +1610,11 @@ mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blit/resolve"); dstTexture->extendLodRangeTo(dstLevel); + + printf("blit(dst = %d, srcLevel = %d, srcLayer = %d, dstOrigin = (%d, %d), src = %d, dstLevel " + "= %d, dstLayer = %d, srcOrigin = (%d, %d), size = (%d, %d))\n", + dst.getId(), srcLevel, srcLayer, dstOrigin.x, dstOrigin.y, src.getId(), dstLevel, + dstLayer, srcOrigin.x, srcOrigin.y, size.x, size.y); } void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers, @@ -1876,25 +1977,58 @@ backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets) { + auto descriptorSet = handle_cast(dsh); + const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount(); + utils::FixedCapacityVector offsetsVector(dynamicBindings, 0); + printf("[DS] bindDescriptorSet(dsh = %d, set = %d, offsets = [", dsh.getId(), set); + for (size_t i = 0; i < dynamicBindings; i++) { + printf("%d", offsets[i]); + if (i < dynamicBindings - 1) { + printf(", "); + } + + offsetsVector[i] = offsets[i]; + } + printf("])\n"); + + // Bind the descriptor set. + mContext->currentDescriptorSets[set] = descriptorSet; + mContext->descriptorBindings.setBuffer(descriptorSet->finalizeAndGetBuffer(this), 0, set); + mContext->dynamicOffsets.setOffsets(set, offsets.data(), dynamicBindings); } void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) << "draw() without a valid command encoder."; + printf("[DS] draw2(...)\n"); - // Bind uniform buffers. - MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; - NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; + // Finalize any descriptor sets that have been bound. + for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { + auto* descriptorSet = mContext->currentDescriptorSets[i]; + if (!descriptorSet) { + continue; + } + auto found = mContext->finalizedDescriptorSets.find(descriptorSet); + if (found == mContext->finalizedDescriptorSets.end()) { + descriptorSet->finalize(this); + mContext->finalizedDescriptorSets.insert(descriptorSet); + } + } - enumerateBoundBuffers(BufferObjectBinding::UNIFORM, - [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer, - uint32_t index) { - uniformsToBind[index] = buffer; - offsets[index] = state.offset; - }); - MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, - UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, - uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT); + // Bind descriptor sets. + mContext->descriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + + // Bind the offset data. + if (mContext->dynamicOffsets.isDirty()) { + const auto [size, data] = mContext->dynamicOffsets.getOffsets(); + [mContext->currentRenderPassEncoder setFragmentBytes:data + length:size * sizeof(uint32_t) + atIndex:25]; + [mContext->currentRenderPassEncoder setVertexBytes:data + length:size * sizeof(uint32_t) + atIndex:25]; + mContext->dynamicOffsets.setDirty(false); + } // Update push constants. for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) { @@ -1908,8 +2042,7 @@ MetalIndexBuffer* indexBuffer = primitive->indexBuffer; - id cmdBuffer = getPendingCommandBuffer(mContext); - id metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer); + id metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(); [mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type) indexCount:indexCount indexType:getIndexType(indexBuffer->elementSize) diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index f10f9f18ec7..fea10a6c254 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -547,18 +547,178 @@ struct MetalTimerQuery : public HwTimerQuery { std::shared_ptr status; }; -// Descriptor sets are layed out as follows: -// Buffers -// Textures +class MetalDescriptorSetLayout : public HwDescriptorSetLayout { +public: + MetalDescriptorSetLayout(DescriptorSetLayout&& layout) noexcept; + + const auto& getBindings() const noexcept { return mLayout.bindings; } + + size_t getDynamicOffsetCount() const noexcept { return mDynamicOffsetCount; } + + id getArgumentEncoderForTextureTypes( + id device, utils::FixedCapacityVector const& textureTypes); + +private: + id getArgumentEncoderForTextureTypesSlow( + id device, utils::FixedCapacityVector const& textureTypes); -struct MetalDescriptorSetLayout : public HwDescriptorSetLayout { - MetalDescriptorSetLayout(DescriptorSetLayout&& layout) noexcept : layout(std::move(layout)) {} - DescriptorSetLayout layout; + DescriptorSetLayout mLayout; + size_t mDynamicOffsetCount = 0; + id mCachedArgumentEncoder = nil; + utils::FixedCapacityVector mCachedTextureTypes; }; struct MetalDescriptorSet : public HwDescriptorSet { - MetalDescriptorSet(Handle layout) noexcept : layout(layout) {} - Handle layout; + MetalDescriptorSet(MetalDescriptorSetLayout* layout) noexcept; + MetalDescriptorSetLayout* layout; + + void finalize(MetalDriver* driver) { + [driver->mContext->currentRenderPassEncoder useResource:driver->mContext->emptyBuffer + usage:MTLResourceUsageRead]; + [driver->mContext->currentRenderPassEncoder useResource:getOrCreateEmptyTexture(driver->mContext) + usage:MTLResourceUsageRead]; + auto const& bindings = this->layout->getBindings(); + for (auto const& binding : bindings) { + switch (binding.type) { + case DescriptorType::UNIFORM_BUFFER: + case DescriptorType::SHADER_STORAGE_BUFFER: { + auto found = buffers.find(binding.binding); + if (found == buffers.end()) { + continue; + } + + auto const& bufferBinding = buffers[binding.binding]; + auto* metalBuffer = driver->handle_cast(bufferBinding.buffer) + ->getBuffer() + ->getGpuBufferForDraw(); + [driver->mContext->currentRenderPassEncoder useResource:metalBuffer + usage:MTLResourceUsageRead]; + break; + } + case DescriptorType::SAMPLER: { + auto found = textures.find(binding.binding); + if (found == textures.end()) { + continue; + } + + auto const& textureBinding = textures[binding.binding]; + auto* texture = driver->handle_cast(textureBinding.texture) + ->getMtlTextureForRead(); + [driver->mContext->currentRenderPassEncoder useResource:texture + usage:MTLResourceUsageRead]; + break; + } + case DescriptorType::INPUT_ATTACHMENT: + break; + } + } + } + + id finalizeAndGetBuffer(MetalDriver* driver) { + if (buffer) { + return buffer; + } + + // Map all the texture bindings to their respective texture types. + auto const& bindings = layout->getBindings(); + auto textureTypes = utils::FixedCapacityVector::with_capacity(bindings.size()); + for (auto const& binding : bindings) { + MTLTextureType textureType = MTLTextureType2D; + if (auto found = textures.find(binding.binding); found != textures.end()) { + auto const& textureBinding = textures[binding.binding]; + auto* texture = driver->handle_cast(textureBinding.texture) + ->getMtlTextureForRead(); + textureType = texture.textureType; + } + textureTypes.push_back(textureType); + } + + MetalContext const& context = *driver->mContext; + + id encoder = + layout->getArgumentEncoderForTextureTypes(context.device, textureTypes); + + buffer = [context.device newBufferWithLength:encoder.encodedLength + options:MTLResourceStorageModeShared]; + [encoder setArgumentBuffer:buffer offset:0]; + + printf("finalizeAndGetBuffer -- encoding begin --\n"); + for (auto const& binding : bindings) { + printf(" binding %d: ", binding.binding); + switch (binding.type) { + case DescriptorType::UNIFORM_BUFFER: + case DescriptorType::SHADER_STORAGE_BUFFER: { + printf("uniform, "); + auto found = buffers.find(binding.binding); + if (found == buffers.end()) { + [encoder setBuffer:driver->mContext->emptyBuffer + offset:0 + atIndex:binding.binding * 2]; + printf("empty buffer\n"); + continue; + } + + auto const& bufferBinding = buffers[binding.binding]; + printf("buffer handle: %d", bufferBinding.buffer.getId()); + auto* metalBuffer = driver->handle_cast(bufferBinding.buffer) + ->getBuffer() + ->getGpuBufferForDraw(); + [encoder setBuffer:metalBuffer + offset:bufferBinding.offset + atIndex:binding.binding * 2]; + break; + } + case DescriptorType::SAMPLER: { + printf("sampler, "); + auto found = textures.find(binding.binding); + if (found == textures.end()) { + [encoder setTexture:driver->mContext->emptyTexture + atIndex:binding.binding * 2]; + id sampler = + driver->mContext->samplerStateCache.getOrCreateState({}); + [encoder setSamplerState:sampler + atIndex:binding.binding * 2 + 1]; + printf("empty texture\n"); + continue; + } + + auto const& textureBinding = textures[binding.binding]; + printf("texture handle: %d", textureBinding.texture.getId()); + auto* texture = driver->handle_cast(textureBinding.texture) + ->getMtlTextureForRead(); + [encoder setTexture:texture atIndex:binding.binding * 2]; + SamplerState samplerState { + .samplerParams = textureBinding.sampler + }; + id sampler = driver->mContext->samplerStateCache.getOrCreateState(samplerState); + [encoder setSamplerState:sampler + atIndex:binding.binding * 2 + 1]; + break; + } + case DescriptorType::INPUT_ATTACHMENT: + assert_invariant(false); + break; + } + printf("\n"); + } + printf("finalizeAndGetBuffer -- end --\n"); + + return buffer; + } + + struct BufferBinding { + BufferObjectHandle buffer; + uint32_t offset; + uint32_t size; + }; + struct TextureBinding { + TextureHandle texture; + SamplerParams sampler; + }; + tsl::robin_map buffers; + tsl::robin_map textures; + + id buffer = nil; }; } // namespace backend diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 991c7ed03d8..8aa622493fc 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -635,12 +635,16 @@ static void func(void* user) { } id MetalTexture::getMtlTextureForRead() noexcept { + // TODO: get LODs to work correctly + id tex = swizzledTextureView ? swizzledTextureView : texture; + return tex; if (lodTextureView) { return lodTextureView; } // The texture's swizzle remains constant throughout its lifetime, however its LOD range can // change. We'll cache the LOD view, and set lodTextureView to nil if minLod or maxLod is // updated. + // TODO: ah, but we can't set it to nil because we need to keep it alive if it has been latched. id t = swizzledTextureView ? swizzledTextureView : texture; if (!t) { return nil; @@ -1340,5 +1344,76 @@ static void func(void* user) { return FenceStatus::ERROR; } +MetalDescriptorSetLayout::MetalDescriptorSetLayout(DescriptorSetLayout&& l) noexcept + : mLayout(std::move(l)) { + size_t dynamicBindings = 0; + for (const auto& binding : mLayout.bindings) { + if (any(binding.flags & DescriptorFlags::DYNAMIC_OFFSET)) { + dynamicBindings++; + } + } + mDynamicOffsetCount = dynamicBindings; +} + +id MetalDescriptorSetLayout::getArgumentEncoderForTextureTypes( + id device, + utils::FixedCapacityVector const& textureTypes) { + if (mCachedArgumentEncoder && + std::equal(textureTypes.begin(), textureTypes.end(), mCachedTextureTypes.begin())) { + return mCachedArgumentEncoder; + } + mCachedArgumentEncoder = getArgumentEncoderForTextureTypesSlow(device, textureTypes); + mCachedTextureTypes = textureTypes; + return mCachedArgumentEncoder; +} + +id MetalDescriptorSetLayout::getArgumentEncoderForTextureTypesSlow( + id device, utils::FixedCapacityVector const& textureTypes) { + auto const& bindings = getBindings(); + NSMutableArray* arguments = [NSMutableArray new]; + // important! the bindings must be sorted by binding number + size_t textureIndex = 0; + for (auto const& binding : bindings) { + switch (binding.type) { + case DescriptorType::UNIFORM_BUFFER: + case DescriptorType::SHADER_STORAGE_BUFFER: { + MTLArgumentDescriptor* bufferArgument = [MTLArgumentDescriptor argumentDescriptor]; + bufferArgument.index = binding.binding * 2; + bufferArgument.dataType = MTLDataTypePointer; + bufferArgument.access = MTLArgumentAccessReadOnly; + [arguments addObject:bufferArgument]; + break; + } + case DescriptorType::SAMPLER: { + MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor]; + textureArgument.index = binding.binding * 2; + textureArgument.dataType = MTLDataTypeTexture; + MTLTextureType textureType = MTLTextureType2D; + if (textureIndex < textureTypes.size()) { + textureType = textureTypes[textureIndex++]; + } + textureArgument.textureType = textureType; + textureArgument.access = MTLArgumentAccessReadOnly; + [arguments addObject:textureArgument]; + + MTLArgumentDescriptor* samplerArgument = [MTLArgumentDescriptor argumentDescriptor]; + samplerArgument.index = binding.binding * 2 + 1; + samplerArgument.dataType = MTLDataTypeSampler; + textureArgument.access = MTLArgumentAccessReadOnly; + [arguments addObject:samplerArgument]; + break; + } + case DescriptorType::INPUT_ATTACHMENT: + // TODO: what to do here? + assert_invariant(false); + break; + } + } + return [device newArgumentEncoderWithArguments:arguments]; +} + +MetalDescriptorSet::MetalDescriptorSet(MetalDescriptorSetLayout* layout) noexcept + : layout(layout) {} + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index 14733730377..d8edaa8a37a 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -39,22 +39,22 @@ namespace backend { // ------------------------------------------------------ // 0 Zero buffer (placeholder vertex buffer) 1 // 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT -// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT -// 26 Push constants 1 -// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT +// 20 Push constants 1 +// 21-24 Descriptor sets (argument buffers) 4 limited by MAX_DESCRIPTOR_SET_COUNT +// 25 Dynamic offset buffer 1 // -// Total 31 +// Total 23 // Compute Bindings // ---------------------- // Bindings Buffer name Count // ------------------------------------------------------ // 0-3 SSBO buffers 4 MAX_SSBO_COUNT -// 17-25 Uniform buffers 9 Program::UNIFORM_BINDING_COUNT -// 26 Push constants 1 -// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT +// 20 Push constants 1 +// 21-24 Descriptor sets (argument buffers) 4 limited by MAX_DESCRIPTOR_SET_COUNT +// 25 Dynamic offset buffer 1 // -// Total 18 +// Total 10 // The total number of vertex buffer "slots" that the Metal backend can bind. // + 1 to account for the zero buffer, a placeholder buffer used internally by the Metal backend. @@ -68,6 +68,7 @@ static constexpr uint32_t ZERO_VERTEX_BUFFER_BINDING = 0u; static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u; // These constants must match the equivalent in CodeGenerator.h. +// TODO: deprecate these static constexpr uint32_t UNIFORM_BUFFER_BINDING_START = 17u; static constexpr uint32_t SSBO_BINDING_START = 0u; static constexpr uint32_t SAMPLER_GROUP_BINDING_START = 27u; @@ -382,14 +383,17 @@ using WindingStateTracker = StateTracker; // Argument encoder struct ArgumentEncoderState { + NSUInteger bufferCount; utils::FixedCapacityVector textureTypes; - explicit ArgumentEncoderState(utils::FixedCapacityVector&& types) - : textureTypes(std::move(types)) {} + explicit ArgumentEncoderState( + NSUInteger bufferCount, utils::FixedCapacityVector&& types) + : bufferCount(bufferCount), textureTypes(std::move(types)) {} bool operator==(const ArgumentEncoderState& rhs) const noexcept { return std::equal(textureTypes.begin(), textureTypes.end(), rhs.textureTypes.begin(), - rhs.textureTypes.end()); + rhs.textureTypes.end()) && + bufferCount == rhs.bufferCount; } bool operator!=(const ArgumentEncoderState& rhs) const noexcept { @@ -411,6 +415,26 @@ struct ArgumentEncoderCreator { using ArgumentEncoderCache = StateCache, ArgumentEncoderCreator, ArgumentEncoderHasher>; +template +class MetalBufferBindings { +public: + MetalBufferBindings() {} + + void invalidate() { + mDirtyBuffers.allOn(); + mDirtyOffsets.allOn(); + } + void setBuffer(const id buffer, NSUInteger offset, NSUInteger index); + void bindBuffers(id encoder, NSUInteger startIndex); + +private: + static_assert(N <= 8); + std::array<__unsafe_unretained id, N> mBuffers = { nil }; + std::array mOffsets = { 0 }; + utils::bitset8 mDirtyBuffers { 0xFF }; + utils::bitset8 mDirtyOffsets { 0xFF }; +}; + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalState.mm b/filament/backend/src/metal/MetalState.mm index 58be435a5bb..104668afa3e 100644 --- a/filament/backend/src/metal/MetalState.mm +++ b/filament/backend/src/metal/MetalState.mm @@ -166,28 +166,40 @@ id ArgumentEncoderCreator::operator()(id device, const ArgumentEncoderState &state) noexcept { const auto& textureTypes = state.textureTypes; - const auto& count = textureTypes.size(); - assert_invariant(count > 0); + const auto& textureCount = textureTypes.size(); + const auto& bufferCount = state.bufferCount; + assert_invariant(textureCount > 0); // Metal has separate data types for textures versus samplers, so the argument buffer layout // alternates between texture and sampler, i.e.: + // buffer0 + // buffer1 // textureA // samplerA // textureB // samplerB // etc NSMutableArray* arguments = - [NSMutableArray arrayWithCapacity:(count * 2)]; - for (size_t i = 0; i < count; i++) { + [NSMutableArray arrayWithCapacity:(bufferCount + textureCount * 2)]; + size_t i = 0; + for (size_t j = 0; j < bufferCount; j++) { + MTLArgumentDescriptor* bufferArgument = [MTLArgumentDescriptor argumentDescriptor]; + bufferArgument.index = i++; + bufferArgument.dataType = MTLDataTypePointer; + bufferArgument.access = MTLArgumentAccessReadOnly; + [arguments addObject:bufferArgument]; + } + + for (size_t j = 0; j < textureCount; j++) { MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor]; - textureArgument.index = i * 2 + 0; + textureArgument.index = i++; textureArgument.dataType = MTLDataTypeTexture; textureArgument.textureType = textureTypes[i]; textureArgument.access = MTLArgumentAccessReadOnly; [arguments addObject:textureArgument]; MTLArgumentDescriptor* samplerArgument = [MTLArgumentDescriptor argumentDescriptor]; - samplerArgument.index = i * 2 + 1; + samplerArgument.index = i++; samplerArgument.dataType = MTLDataTypeSampler; textureArgument.access = MTLArgumentAccessReadOnly; [arguments addObject:samplerArgument]; @@ -196,5 +208,48 @@ return [device newArgumentEncoderWithArguments:arguments]; } +template +void MetalBufferBindings::setBuffer(const id buffer, NSUInteger offset, NSUInteger index) { + assert_invariant(offset + 1 <= N); + + if (mBuffers[index] != buffer) { + mBuffers[index] = buffer; + mDirtyBuffers.set(index); + } + + if (mOffsets[index] != offset) { + mOffsets[index] = offset; + mDirtyOffsets.set(index); + } +} + +template +void MetalBufferBindings::bindBuffers(id encoder, NSUInteger startIndex) { + if (mDirtyBuffers.none() && mDirtyOffsets.none()) { + return; + } + + utils::bitset8 onlyOffsetDirty = mDirtyOffsets & ~mDirtyBuffers; + onlyOffsetDirty.forEachSetBit([&](size_t i) { + [(id)encoder setFragmentBufferOffset:mOffsets[i] + atIndex:i + startIndex]; + [(id)encoder setVertexBufferOffset:mOffsets[i] + atIndex:i + startIndex]; + }); + mDirtyOffsets.reset(); + + mDirtyBuffers.forEachSetBit([&](size_t i) { + [(id)encoder setFragmentBuffer:mBuffers[i] + offset:mOffsets[i] + atIndex:i + startIndex]; + [(id)encoder setVertexBuffer:mBuffers[i] + offset:mOffsets[i] + atIndex:i + startIndex]; + }); + mDirtyBuffers.reset(); +} + +template class MetalBufferBindings<4>; + } // namespace backend } // namespace filament diff --git a/filament/backend/test/MetalTest.mm b/filament/backend/test/MetalTest.mm new file mode 100644 index 00000000000..1fda63f0c12 --- /dev/null +++ b/filament/backend/test/MetalTest.mm @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "../src/metal/MetalContext.h" + +namespace test { + +TEST(MetalDynamicOffsets, none) { + filament::backend::MetalDynamicOffsets dynamicOffsets; + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 0u); +} + +TEST(MetalDynamicOffsets, basic) { + filament::backend::MetalDynamicOffsets dynamicOffsets; + { + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 0u); + } + + { + uint32_t o[2] = { 1, 2 }; + dynamicOffsets.setOffsets(0, o, 2); + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 2); + EXPECT_EQ(offsets[0], 1); + EXPECT_EQ(offsets[1], 2); + } + + { + uint32_t o[3] = { 3, 4, 5 }; + dynamicOffsets.setOffsets(1, o, 3); + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 5); + EXPECT_EQ(offsets[0], 1); + EXPECT_EQ(offsets[1], 2); + EXPECT_EQ(offsets[2], 3); + EXPECT_EQ(offsets[3], 4); + EXPECT_EQ(offsets[4], 5); + } + + // skip descriptor set index 2 + + { + uint32_t o[1] = { 6 }; + dynamicOffsets.setOffsets(3, o, 1); + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 6); + EXPECT_EQ(offsets[0], 1); + EXPECT_EQ(offsets[1], 2); + EXPECT_EQ(offsets[2], 3); + EXPECT_EQ(offsets[3], 4); + EXPECT_EQ(offsets[4], 5); + EXPECT_EQ(offsets[5], 6); + } +} + +TEST(MetalDynamicOffsets, outOfOrder) { + filament::backend::MetalDynamicOffsets dynamicOffsets; + uint32_t o1[2] = { 2, 3 }; + dynamicOffsets.setOffsets(1, o1, 2); + uint32_t o2[2] = { 0, 1 }; + dynamicOffsets.setOffsets(0, o2, 2); + uint32_t o3[2] = { 4, 5 }; + dynamicOffsets.setOffsets(2, o3, 2); + + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 6); + EXPECT_EQ(offsets[0], 0); + EXPECT_EQ(offsets[1], 1); + EXPECT_EQ(offsets[2], 2); + EXPECT_EQ(offsets[3], 3); + EXPECT_EQ(offsets[4], 4); + EXPECT_EQ(offsets[5], 5); +}; + +TEST(MetalDynamicOffsets, removal) { + filament::backend::MetalDynamicOffsets dynamicOffsets; + uint32_t o1[2] = { 2, 3 }; + dynamicOffsets.setOffsets(1, o1, 2); + uint32_t o2[2] = { 0, 1 }; + dynamicOffsets.setOffsets(0, o2, 2); + uint32_t o3[2] = { 4, 5 }; + dynamicOffsets.setOffsets(2, o3, 2); + dynamicOffsets.setOffsets(1, nullptr, 0); + + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 4); + EXPECT_EQ(offsets[0], 0); + EXPECT_EQ(offsets[1], 1); + EXPECT_EQ(offsets[2], 4); + EXPECT_EQ(offsets[3], 5); +}; + +TEST(MetalDynamicOffsets, resize) { + filament::backend::MetalDynamicOffsets dynamicOffsets; + uint32_t o1[2] = { 2, 3 }; + dynamicOffsets.setOffsets(1, o1, 2); + uint32_t o2[2] = { 0, 1 }; + dynamicOffsets.setOffsets(0, o2, 2); + uint32_t o3[2] = { 6, 7 }; + dynamicOffsets.setOffsets(2, o3, 2); + uint32_t o4[4] = { 2, 3, 4, 5 }; + dynamicOffsets.setOffsets(1, o4, 4); + + const auto [count, offsets] = dynamicOffsets.getOffsets(); + EXPECT_EQ(count, 8); + EXPECT_EQ(offsets[0], 0); + EXPECT_EQ(offsets[1], 1); + EXPECT_EQ(offsets[2], 2); + EXPECT_EQ(offsets[3], 3); + EXPECT_EQ(offsets[4], 4); + EXPECT_EQ(offsets[5], 5); + EXPECT_EQ(offsets[6], 6); + EXPECT_EQ(offsets[7], 7); +}; + +TEST(MetalDynamicOffsets, dirty) { + filament::backend::MetalDynamicOffsets dynamicOffsets; + EXPECT_FALSE(dynamicOffsets.isDirty()); + + uint32_t o1[2] = { 2, 3 }; + dynamicOffsets.setOffsets(1, o1, 2); + EXPECT_TRUE(dynamicOffsets.isDirty()); + + dynamicOffsets.setDirty(false); + EXPECT_FALSE(dynamicOffsets.isDirty()); + + // Setting the same offsets should not mark the offsets as dirty + dynamicOffsets.setOffsets(1, o1, 2); + EXPECT_FALSE(dynamicOffsets.isDirty()); + + // Resizing the offsets should mark the offsets as dirty + uint32_t o2[3] = { 4, 5, 6 }; + dynamicOffsets.setOffsets(1, o2, 3); + EXPECT_TRUE(dynamicOffsets.isDirty()); +}; + +}; + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/filament/backend/test/ShaderGenerator.cpp b/filament/backend/test/ShaderGenerator.cpp index 7d0ad716e1d..d8e6939fade 100644 --- a/filament/backend/test/ShaderGenerator.cpp +++ b/filament/backend/test/ShaderGenerator.cpp @@ -73,12 +73,13 @@ void ShaderGenerator::shutdown() { FinalizeProcess(); } -ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, - Backend backend, bool isMobile, const filament::SamplerInterfaceBlock* sib) noexcept - : mBackend(backend), - mVertexBlob(transpileShader(ShaderStage::VERTEX, std::move(vertex), backend, isMobile, sib)), - mFragmentBlob(transpileShader(ShaderStage::FRAGMENT, std::move(fragment), backend, - isMobile, sib)) { +ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, Backend backend, + bool isMobile, filamat::DescriptorSetInfoVector&& descriptorSets) noexcept + : mBackend(backend), + mVertexBlob(transpileShader( + ShaderStage::VERTEX, std::move(vertex), backend, isMobile, descriptorSets)), + mFragmentBlob(transpileShader( + ShaderStage::FRAGMENT, std::move(fragment), backend, isMobile, descriptorSets)) { switch (backend) { case Backend::OPENGL: mShaderLanguage = filament::backend::ShaderLanguage::ESSL3; @@ -95,9 +96,8 @@ ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, } } -ShaderGenerator::Blob ShaderGenerator::transpileShader( - ShaderStage stage, std::string shader, Backend backend, bool isMobile, - const filament::SamplerInterfaceBlock* sib) noexcept { +ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::string shader, + Backend backend, bool isMobile, const filamat::DescriptorSetInfoVector& descriptorSets) noexcept { TProgram program; const EShLanguage language = stage == ShaderStage::VERTEX ? EShLangVertex : EShLangFragment; TShader tShader(language); @@ -161,9 +161,7 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader( return { result.c_str(), result.c_str() + result.length() + 1 }; } else if (backend == Backend::METAL) { const auto sm = isMobile ? ShaderModel::MOBILE : ShaderModel::DESKTOP; - filamat::SibVector sibs = filamat::SibVector::with_capacity(1); - if (sib) { sibs.emplace_back(0, sib); } - filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, sibs, nullptr); + filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, descriptorSets, nullptr); return { result.c_str(), result.c_str() + result.length() + 1 }; } else if (backend == Backend::VULKAN) { return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) }; diff --git a/filament/backend/test/ShaderGenerator.h b/filament/backend/test/ShaderGenerator.h index 39a87a7f208..31ed6c3d5a1 100644 --- a/filament/backend/test/ShaderGenerator.h +++ b/filament/backend/test/ShaderGenerator.h @@ -22,6 +22,7 @@ #include "private/filament/SamplerInterfaceBlock.h" #include "private/backend/DriverApi.h" #include "backend/Program.h" +#include "../src/GLSLPostProcessor.h" #include @@ -40,7 +41,7 @@ class ShaderGenerator { * @param fragment The fragment shader, written in GLSL 450 core. */ ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile, - const filament::SamplerInterfaceBlock* sib = nullptr) noexcept; + filamat::DescriptorSetInfoVector&& descriptorSets = {}) noexcept; ShaderGenerator(const ShaderGenerator& rhs) = delete; ShaderGenerator& operator=(const ShaderGenerator& rhs) = delete; @@ -52,7 +53,7 @@ class ShaderGenerator { using Blob = std::vector; static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend, - bool isMobile, const filament::SamplerInterfaceBlock* sib = nullptr) noexcept; + bool isMobile, const filamat::DescriptorSetInfoVector& descriptorSets) noexcept; Backend mBackend; diff --git a/filament/backend/test/mac_runner.mm b/filament/backend/test/mac_runner.mm index 99205e2e7b6..1aab059fb11 100644 --- a/filament/backend/test/mac_runner.mm +++ b/filament/backend/test/mac_runner.mm @@ -53,8 +53,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { nativeView.width = static_cast(drawableSize.width); nativeView.height = static_cast(drawableSize.height); - test::runTests(); - // exit(runTests()); + exit(test::runTests()); } - (NSView*)createView { diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index 599ac9e71e4..3f342ff7ada 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -38,7 +38,7 @@ using namespace utils; static const char* const triangleVs = R"(#version 450 core layout(location = 0) in vec4 mesh_position; -uniform Params { highp vec4 color; highp vec4 scale; } params; +layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params; void main() { gl_Position = vec4((mesh_position.xy + 0.5) * params.scale.xy, params.scale.z, 1.0); #if defined(TARGET_VULKAN_ENVIRONMENT) @@ -50,7 +50,7 @@ void main() { static const char* const triangleFs = R"(#version 450 core precision mediump int; precision highp float; layout(location = 0) out vec4 fragColor; -uniform Params { highp vec4 color; highp vec4 scale; } params; +layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params; void main() { fragColor = params.color; })"; @@ -348,9 +348,15 @@ TEST_F(BackendTest, ColorResolve) { // Create a program. ProgramHandle program; { - ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform); + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0}, {}} + }} + }; + ShaderGenerator shaderGen( + triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors)); Program prog = shaderGen.getProgram(api); - prog.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); + prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); program = api.createProgram(std::move(prog)); } @@ -412,7 +418,7 @@ TEST_F(BackendTest, ColorResolve) { }); api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams)); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); // FIXME: on Metal this triangle is not drawn. Can't understand why. api.beginFrame(0, 0, 0); diff --git a/filament/backend/test/test_BufferUpdates.cpp b/filament/backend/test/test_BufferUpdates.cpp index e93b088116a..76ac2374e1c 100644 --- a/filament/backend/test/test_BufferUpdates.cpp +++ b/filament/backend/test/test_BufferUpdates.cpp @@ -31,7 +31,7 @@ layout(location = 0) in vec4 mesh_position; layout(location = 0) out uvec4 indices; -uniform Params { +layout(binding = 0, set = 0) uniform Params { highp vec4 padding[4]; // offset of 64 bytes highp vec4 color; @@ -96,7 +96,13 @@ TEST_F(BackendTest, VertexBufferUpdate) { api.makeCurrent(swapChain, swapChain); // Create a program. - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform); + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0}, {}} + }} + }; + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program p = shaderGen.getProgram(api); p.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); auto program = api.createProgram(std::move(p)); @@ -220,7 +226,13 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { api.makeCurrent(swapChain, swapChain); // Create a program. - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform); + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0}, {}} + }} + }; + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program p = shaderGen.getProgram(api); p.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); auto program = api.createProgram(std::move(p)); diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index c30d6604545..1cc30049227 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -55,14 +55,15 @@ layout(location = 0) out vec4 fragColor; // Filament's Vulkan backend requires a descriptor set index of 1 for all samplers. // This parameter is ignored for other backends. -layout(location = 0, set = 1) uniform sampler2D test_tex; +layout(binding = 0, set = 1) uniform sampler2D test_tex; -uniform Params { +layout(binding = 1, set = 1) uniform Params { highp float fbWidth; highp float fbHeight; highp float sourceLevel; highp float unused; } params; + void main() { vec2 fbsize = vec2(params.fbWidth, params.fbHeight); vec2 uv = (gl_FragCoord.xy + 0.5) / fbsize; @@ -139,14 +140,18 @@ TEST_F(BackendTest, FeedbackLoops) { // Create a program. ProgramHandle program; { - SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); - ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, &sib); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo}, + {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 1}, {}} + }} + }; + ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, + std::move(descriptors)); Program prog = shaderGen.getProgram(api); - prog.descriptorBindings(0, { + prog.descriptorBindings(1, { { "test_tex", DescriptorType::SAMPLER, 0 }, { "Params", DescriptorType::UNIFORM_BUFFER, 1 } }); @@ -183,7 +188,7 @@ TEST_F(BackendTest, FeedbackLoops) { BufferObjectBinding::UNIFORM, BufferUsage::STATIC); api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams)); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); // Create a RenderTarget for each miplevel. Handle renderTargets[kNumLevels]; diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index 8e7e3df7f3a..fa2f15b1180 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -301,18 +301,21 @@ TEST_F(BackendTest, UpdateImage2D) { auto defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a program. - SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH }} ) - .build(); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(t.textureFormat), fragmentTemplate); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program prog = shaderGen.getProgram(api); - prog.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}}); ProgramHandle const program = api.createProgram(std::move(prog)); @@ -349,7 +352,7 @@ TEST_F(BackendTest, UpdateImage2D) { .filterMag = SamplerMagFilter::NEAREST, .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); @@ -386,16 +389,19 @@ TEST_F(BackendTest, UpdateImageSRGB) { auto defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a program. - SamplerInterfaceBlock const sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentTemplate); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program prog = shaderGen.getProgram(api); - prog.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}}); ProgramHandle const program = api.createProgram(std::move(prog)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ {{ @@ -441,7 +447,7 @@ TEST_F(BackendTest, UpdateImageSRGB) { .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); @@ -479,16 +485,19 @@ TEST_F(BackendTest, UpdateImageMipLevel) { auto defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a program. - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentUpdateImageMip); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program prog = shaderGen.getProgram(api); - prog.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}}); ProgramHandle const program = api.createProgram(std::move(prog)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ {{ @@ -518,7 +527,7 @@ TEST_F(BackendTest, UpdateImageMipLevel) { .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); @@ -558,16 +567,19 @@ TEST_F(BackendTest, UpdateImage3D) { auto defaultRenderTarget = api.createDefaultRenderTarget(0); // Create a program. - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_2D_ARRAY, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; std::string fragment = stringReplace("{samplerType}", getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program prog = shaderGen.getProgram(api); - prog.descriptorBindings(0, {{ "test_tex", DescriptorType::SAMPLER, 0 }}); + prog.descriptorBindings(1, {{ "test_tex", DescriptorType::SAMPLER, 0 }}); ProgramHandle const program = api.createProgram(std::move(prog)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ {{ @@ -607,7 +619,7 @@ TEST_F(BackendTest, UpdateImage3D) { .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp index 4ced788c0df..cb15e1923fd 100644 --- a/filament/backend/test/test_MipLevels.cpp +++ b/filament/backend/test/test_MipLevels.cpp @@ -98,12 +98,14 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Create a program that samples a texture. Handle textureProgram; { - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("backend_test_sib") - .stageFlags(backend::ShaderStageFlags::FRAGMENT) - .add( {{"tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "backend_test", "sib_tex", 0, + SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"backend_test_sib_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; + ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program p = shaderGen.getProgram(api); p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}}); textureProgram = api.createProgram(std::move(p)); diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index 3c8a7dfee8e..bbc61f44885 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -71,16 +71,19 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { auto swapChain = createSwapChain(); - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); // Create a program that samples a texture. Program p = shaderGen.getProgram(api); - p.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}}); backend::Handle program = api.createProgram(std::move(p)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ {{ @@ -126,7 +129,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { api.beginFrame(0, 0, 0); api.updateDescriptorSetTexture(descriptorSet, 0, texture, {}); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); // Render a triangle. api.beginRenderPass(defaultRenderTarget, params); @@ -158,16 +161,19 @@ TEST_F(BackendTest, RenderExternalImage) { auto swapChain = createSwapChain(); - SamplerInterfaceBlock sib = filament::SamplerInterfaceBlock::Builder() - .name("Test") - .stageFlags(backend::ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) - .add( {{"tex", 0, SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH }} ) - .build(); - ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, &sib); + filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, + SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false }; + filamat::DescriptorSetInfoVector descriptors = { + {1, { + {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} + }} + }; + ShaderGenerator shaderGen( + vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); // Create a program that samples a texture. Program p = shaderGen.getProgram(api); - p.descriptorBindings(0, {{"test_tex", DescriptorType::SAMPLER, 0}}); + p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}}); auto program = api.createProgram(std::move(p)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ @@ -257,7 +263,7 @@ TEST_F(BackendTest, RenderExternalImage) { api.updateDescriptorSetTexture(descriptorSet, 0, texture, {}); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); // Render a triangle. api.beginRenderPass(defaultRenderTarget, params); diff --git a/filament/src/DFG.cpp b/filament/src/DFG.cpp index 933d97d525b..64d18d9bd6a 100644 --- a/filament/src/DFG.cpp +++ b/filament/src/DFG.cpp @@ -31,11 +31,13 @@ void DFG::init(FEngine& engine) noexcept { static_assert(sizeof(DFG_LUT) == byteCount, "DFG_LUT_SIZE doesn't match size of the DFG LUT"); + printf("DFG LUT --begin--:\n"); Texture* lut = Texture::Builder() .width(DFG_LUT_SIZE) .height(DFG_LUT_SIZE) .format(backend::TextureFormat::RGB16F) .build(engine); + printf("DFG LUT --end--:\n"); Texture::PixelBufferDescriptor buffer(DFG_LUT, byteCount, Texture::Format::RGB, Texture::Type::HALF); diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 3734df4b6d9..4994cf14a24 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -243,6 +243,8 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { mHandle = driver.importTexture(builder->mImportedId, mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage); } + + printf("FTexture::FTexture, mHandle = %d\n", mHandle.getId()); } // frees driver resources, object becomes invalid diff --git a/libs/filabridge/src/DescriptorSets.cpp b/libs/filabridge/src/DescriptorSets.cpp index a9909fb01ba..9cc7e768803 100644 --- a/libs/filabridge/src/DescriptorSets.cpp +++ b/libs/filabridge/src/DescriptorSets.cpp @@ -81,6 +81,7 @@ static DescriptorSetLayout perViewDescriptorSetLayout = {{ { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FOG, DescriptorFlags::NONE, 0 }, }}; + static DescriptorSetLayout perRenderableDescriptorSetLayout = {{ { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET, 0 }, { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET, 0 }, diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index c1e2a7092da..ab74cfc0768 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -24,6 +24,7 @@ #include #include "backend/DriverEnums.h" +#include "private/filament/DescriptorSets.h" #include "sca/builtinResource.h" #include "sca/GLSLTools.h" @@ -57,6 +58,43 @@ namespace msl { // this is only used for MSL using BindingIndexMap = std::unordered_map; +// I want to know: + +// 0. DescriptorSetBindingPoint::PER_VIEW +// which Ubos (there may be more than 1)? ~and which are in use~ (we assume all are in use) +// for each UBO (which needs to be in order): +// what's the name of the pointer +// what's the type of Struct it points to? +// for example: +// frameUniforms, FrameUniforms +// shadowUniforms, ShadowUniforms +// etc +// which Sibs (there is only ever 1) +// what's the name of the sampler +// what's the type of the sampler + +// 1. DescriptorSetBindingPoint::PER_RENDERABLE +// which Ubos (there may be more than 1) and which are in use? +// which Sibs (there is only ever 1) + +// 2. DescriptorSetBindingPoint::PER_MATERIAL +// which Ubos (there may be more than 1) and which are in use? +// which Sibs (there is only ever 1) + +// In order to get the ubos, I can do: +// descriptor_sets = {} // automatically add set key to empty [] array if key/value pair doesn't exist +// for each ubo inside Ubo: +// binding = getBinding(ubo) +// interface_block = get(ubo) +// descriptor_set[binding.set].push_back((binding.binding, interface_block)) +// for each descriptor_set inside descriptor_sets: +// sort the descriptor_set by binding +// for each (binding, interface_block) in descriptor_set: +// type_name = interface_block.getName() # e.g., FrameUniforms +// entry_name = type_name.first_letter_lowercase() + +// When Filament goes to create an arg buffer, will it create one that only contains the uniforms in use? + //static void collectSibs(const GLSLPostProcessor::Config& config, SibVector& sibs) { // switch (config.domain) { // case MaterialDomain::SURFACE: @@ -79,6 +117,212 @@ using BindingIndexMap = std::unordered_map; // &config.materialInfo->sib); //} +const char* prettyDescriptorType(DescriptorType type) { + switch (type) { + case DescriptorType::UNIFORM_BUFFER: return "UNIFORM_BUFFER"; + case DescriptorType::SHADER_STORAGE_BUFFER: return "SHADER_STORAGE_BUFFER"; + case DescriptorType::SAMPLER: return "SAMPLER"; + case DescriptorType::INPUT_ATTACHMENT: return "INPUT_ATTACHMENT"; + } +} + +const char* prettyShaderStageFlags(ShaderStageFlags flags) { + std::vector stages; + if (any(flags & ShaderStageFlags::VERTEX)) { + stages.push_back("VERTEX"); + } + if (any(flags & ShaderStageFlags::FRAGMENT)) { + stages.push_back("FRAGMENT"); + } + if (any(flags & ShaderStageFlags::COMPUTE)) { + stages.push_back("COMPUTE"); + } + if (stages.empty()) { + return "NONE"; + } + static char buffer[64]; + buffer[0] = '\0'; + for (size_t i = 0; i < stages.size(); i++) { + if (i > 0) { + strcat(buffer, " | "); + } + strcat(buffer, stages[i]); + } + return buffer; +} + +const char* prettyDescriptorFlags(DescriptorFlags flags) { + if (flags == DescriptorFlags::DYNAMIC_OFFSET) { + return "DYNAMIC_OFFSET"; + } + return "NONE"; +} + +const char* prettyPrintSamplerType(SamplerType type) { + switch (type) { + case SamplerType::SAMPLER_2D: return "SAMPLER_2D"; + case SamplerType::SAMPLER_2D_ARRAY: return "SAMPLER_2D_ARRAY"; + case SamplerType::SAMPLER_CUBEMAP: return "SAMPLER_CUBEMAP"; + case SamplerType::SAMPLER_EXTERNAL: return "SAMPLER_EXTERNAL"; + case SamplerType::SAMPLER_3D: return "SAMPLER_3D"; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: return "SAMPLER_CUBEMAP_ARRAY"; + } +} + +DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib) noexcept { + auto const& samplers = sib.getSamplerInfoList(); + + DescriptorSetLayout layout; + layout.bindings.reserve(1 + samplers.size()); + + layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, + +PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 }); + + for (auto const& sampler : samplers) { + layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::SAMPLER, + ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, sampler.binding, + DescriptorFlags::NONE, 0 }); + } + + return layout; +} + +static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set, + const GLSLPostProcessor::Config& config, DescriptorSetInfo& descriptors) { + const MaterialInfo& material = *config.materialInfo; + + DescriptorSetLayout const info = [&]() { + switch (set) { + case DescriptorSetBindingPoints::PER_VIEW: { + if (filament::Variant::isValidDepthVariant(config.variant)) { + return descriptor_sets::getDepthVariantLayout(); + } + if (filament::Variant::isSSRVariant(config.variant)) { + return descriptor_sets::getSsrVariantLayout(); + } + return descriptor_sets::getPerViewDescriptorSetLayout(config.domain, + config.variantFilter, material.isLit, material.reflectionMode, + material.refractionMode); + } + case DescriptorSetBindingPoints::PER_RENDERABLE: + return descriptor_sets::getPerRenderableLayout(); + case DescriptorSetBindingPoints::PER_MATERIAL: + return getPerMaterialDescriptorSet(config.materialInfo->sib); + default: + return DescriptorSetLayout {}; + } + }(); + + auto samplerList = [&]() { + switch (set) { + case DescriptorSetBindingPoints::PER_VIEW: + return SibGenerator::getPerViewSib(config.variant).getSamplerInfoList(); + case DescriptorSetBindingPoints::PER_RENDERABLE: + return SibGenerator::getPerRenderableSib(config.variant).getSamplerInfoList(); + case DescriptorSetBindingPoints::PER_MATERIAL: + return config.materialInfo->sib.getSamplerInfoList(); + default: + return SamplerInterfaceBlock::SamplerInfoList {}; + } + }(); + + // remove all the samplers that are not included in the descriptor-set layout + samplerList.erase(std::remove_if(samplerList.begin(), samplerList.end(), + [&info](auto const& entry) { + auto pos = std::find_if(info.bindings.begin(), + info.bindings.end(), [&entry](const auto& item) { + return item.binding == entry.binding; + }); + return pos == info.bindings.end(); + }), + samplerList.end()); + + auto getDescriptorName = [&](DescriptorSetBindingPoints set, descriptor_binding_t binding) { + if (set == DescriptorSetBindingPoints::PER_MATERIAL) { + auto pos = std::find_if(samplerList.begin(), samplerList.end(), + [&](const auto& entry) { return entry.binding == binding; }); + if (pos == samplerList.end()) { + return descriptor_sets::getDescriptorName(set, binding); + } + SamplerInterfaceBlock::SamplerInfo& sampler = *pos; + return sampler.uniformName; + } + return descriptor_sets::getDescriptorName(set, binding); + }; + + for (size_t i = 0; i < info.bindings.size(); i++) { + backend::descriptor_binding_t binding = info.bindings[i].binding; + auto name = getDescriptorName(set, binding); + if (info.bindings[i].type == DescriptorType::SAMPLER) { + auto pos = std::find_if(samplerList.begin(), samplerList.end(), + [&](const auto& entry) { return entry.binding == binding; }); + assert_invariant(pos != samplerList.end()); + SamplerInterfaceBlock::SamplerInfo& sampler = *pos; + descriptors.emplace_back(name, info.bindings[i], sampler); + } else { + descriptors.emplace_back(name, info.bindings[i], std::nullopt); + } + } +} + +void prettyPrintDescriptorSetInfoVector(DescriptorSetInfoVector const& sets) { + auto getName = [](uint8_t set) { + switch (set) { + case +DescriptorSetBindingPoints::PER_VIEW: + return "perViewDescriptorSetLayout"; + case +DescriptorSetBindingPoints::PER_RENDERABLE: + return "perRenderableDescriptorSetLayout"; + case +DescriptorSetBindingPoints::PER_MATERIAL: + return "perMaterialDescriptorSetLayout"; + default: + return "unknown"; + } + }; + for (auto const& set : sets) { + auto const& [setIndex, descriptors] = set; + printf("[DS] info (%s) = [\n", getName(setIndex)); + for (auto const& descriptor : descriptors) { + auto const& [name, info, sampler] = descriptor; + if (info.type == DescriptorType::SAMPLER) { + assert_invariant(sampler.has_value()); + printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = " + "%s, samplerType = %s}", + name.c_str_safe(), info.binding, prettyDescriptorType(info.type), + info.count, prettyShaderStageFlags(info.stageFlags), + prettyDescriptorFlags(info.flags), prettyPrintSamplerType(sampler->type)); + } else { + printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = " + "%s}", + name.c_str_safe(), info.binding, prettyDescriptorType(info.type), + info.count, prettyShaderStageFlags(info.stageFlags), + prettyDescriptorFlags(info.flags)); + } + printf(",\n"); + } + printf("]\n"); + } +} + +// TODO: maybe rename to collectDescriptorSets +static void collectDescriptors( + const GLSLPostProcessor::Config& config, DescriptorSetInfoVector& sets) { + auto perViewDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); + collectDescriptorsForSet(DescriptorSetBindingPoints::PER_VIEW, config, perViewDescriptors); + sets.emplace_back(+DescriptorSetBindingPoints::PER_VIEW, std::move(perViewDescriptors)); + + auto perRenderableDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); + collectDescriptorsForSet( + DescriptorSetBindingPoints::PER_RENDERABLE, config, perRenderableDescriptors); + sets.emplace_back( + +DescriptorSetBindingPoints::PER_RENDERABLE, std::move(perRenderableDescriptors)); + + auto perMaterialDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); + collectDescriptorsForSet( + DescriptorSetBindingPoints::PER_MATERIAL, config, perMaterialDescriptors); + sets.emplace_back(+DescriptorSetBindingPoints::PER_MATERIAL, std::move(perMaterialDescriptors)); +} + } // namespace msl GLSLPostProcessor::GLSLPostProcessor(MaterialBuilder::Optimization optimization, uint32_t flags) @@ -140,10 +384,9 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const return oss.str(); } -void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl, - filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, const SibVector& sibs, - const ShaderMinifier* minifier) { - +void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, + filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, + const DescriptorSetInfoVector& descriptorSets, const ShaderMinifier* minifier) { using namespace msl; CompilerMSL mslCompiler(*spirv); @@ -170,35 +413,12 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl, mslOptions.argument_buffers = true; mslOptions.ios_support_base_vertex_instance = true; - - // We're using argument buffers for texture resources, however, we cannot rely on spirv-cross to - // generate the argument buffer definitions. - // - // Consider a shader with 3 textures: - // layout (set = 0, binding = 0) uniform sampler2D texture1; - // layout (set = 0, binding = 1) uniform sampler2D texture2; - // layout (set = 0, binding = 2) uniform sampler2D texture3; - // - // If only texture1 and texture2 are used in the material, then texture3 will be optimized away. - // This results in an argument buffer like the following: - // struct spvDescriptorSetBuffer0 { - // texture2d texture1 [[id(0)]]; - // sampler texture1Smplr [[id(1)]]; - // texture2d texture2 [[id(2)]]; - // sampler texture2Smplr [[id(3)]]; - // }; - // Note that this happens even if "pad_argument_buffer_resources" and - // "force_active_argument_buffer_resources" are true. - // - // This would be fine, except older Apple devices don't like it when the argument buffer in the - // shader doesn't precisely match the one generated at runtime. - // - // So, we use the MetalArgumentBuffer class to replace spirv-cross' argument buffer definitions - // with our own that contain all the textures/samples, even those optimized away. - std::vector argumentBuffers; + mslOptions.dynamic_offsets_buffer_index = 25; mslCompiler.set_msl_options(mslOptions); + + auto executionModel = mslCompiler.get_execution_model(); // Metal Descriptor Sets @@ -283,6 +503,100 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob *spirv, std::string *outMsl, // from becoming argument buffers. // mslCompiler.add_discrete_descriptor_set(0); + // Map each descriptor set (argument buffer) to a [[buffer(n)]] binding. + // For example, mapDescriptorSet(0, 21) says "map descriptor set 0 to [[buffer(21)]]" + auto mapDescriptorSet = [&mslCompiler](uint32_t set, uint32_t buffer) { + MSLResourceBinding argBufferBinding; + argBufferBinding.basetype = SPIRType::BaseType::Float; + argBufferBinding.stage = mslCompiler.get_execution_model(); + argBufferBinding.desc_set = set; + argBufferBinding.binding = kArgumentBufferBinding; + argBufferBinding.count = 1; + argBufferBinding.msl_buffer = buffer; + mslCompiler.add_msl_resource_binding(argBufferBinding); + }; + mapDescriptorSet(0, 21); + mapDescriptorSet(1, 22); + mapDescriptorSet(2, 23); + mapDescriptorSet(3, 24); + + auto resources = mslCompiler.get_shader_resources(); + + // We're using argument buffers for descriptor sets, however, we cannot rely on spirv-cross to + // generate the argument buffer definitions. + // + // Consider a shader with 3 textures: + // layout (set = 0, binding = 0) uniform sampler2D texture1; + // layout (set = 0, binding = 1) uniform sampler2D texture2; + // layout (set = 0, binding = 2) uniform sampler2D texture3; + // + // If only texture1 and texture2 are used in the material, then texture3 will be optimized away. + // This results in an argument buffer like the following: + // struct spvDescriptorSetBuffer0 { + // texture2d texture1 [[id(0)]]; + // sampler texture1Smplr [[id(1)]]; + // texture2d texture2 [[id(2)]]; + // sampler texture2Smplr [[id(3)]]; + // }; + // Note that this happens even if "pad_argument_buffer_resources" and + // "force_active_argument_buffer_resources" are true. + // + // This would be fine, except older Apple devices don't like it when the argument buffer in the + // shader doesn't precisely match the one generated at runtime. + // + // So, we use the MetalArgumentBuffer class to replace spirv-cross' argument buffer definitions + // with our own that contain all the descriptors, even those optimized away. + std::vector argumentBuffers; + size_t dynamicOffsetsBufferIndex = 0; + for (auto const& set : descriptorSets) { + auto const& [setIndex, descriptors] = set; + auto argBufferBuilder = MetalArgumentBuffer::Builder().name( + "spvDescriptorSetBuffer" + std::to_string(int(setIndex))); + for (auto const& descriptor : descriptors) { + auto const& [name, info, sampler] = descriptor; + // TODO: Handle INPUT_ATTACHMENT case + switch (info.type) { + case DescriptorType::UNIFORM_BUFFER: + case DescriptorType::SHADER_STORAGE_BUFFER: { + std::string lowercasedName = name.c_str(); + assert_invariant(!lowercasedName.empty()); + lowercasedName[0] = std::tolower(lowercasedName[0]); + argBufferBuilder + .buffer(info.binding * 2 + 0, name.c_str(), lowercasedName); + if (any(info.flags & DescriptorFlags::DYNAMIC_OFFSET)) { + // TODO: this requires that the sets and descriptors are sorted (at least + // the uniforms) + mslCompiler.add_dynamic_buffer( + setIndex, info.binding * 2 + 0, dynamicOffsetsBufferIndex++); + } + break; + } + + case DescriptorType::SAMPLER: { + assert_invariant(sampler.has_value()); + const std::string samplerName = std::string(name.c_str()) + "Smplr"; + argBufferBuilder + .texture(info.binding * 2 + 0, name.c_str(), sampler->type, + sampler->format, sampler->multisample) + .sampler(info.binding * 2 + 1, samplerName); + break; + } + } + } + argumentBuffers.push_back(argBufferBuilder.build()); + } + + // Bind push constants to [buffer(26)] + MSLResourceBinding pushConstantBinding; + // the baseType doesn't matter, but can't be UNKNOWN + pushConstantBinding.basetype = SPIRType::BaseType::Struct; + pushConstantBinding.stage = executionModel; + pushConstantBinding.desc_set = kPushConstDescSet; + pushConstantBinding.binding = kPushConstBinding; + pushConstantBinding.count = 1; + pushConstantBinding.msl_buffer = 20; + mslCompiler.add_msl_resource_binding(pushConstantBinding); + *outMsl = mslCompiler.compile(); if (minifier) { *outMsl = minifier->removeWhitespace(*outMsl); @@ -383,9 +697,18 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co fixupClipDistance(*internalConfig.spirvOutput, config); if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); -// msl::collectSibs(config, sibs); + auto descriptors = + DescriptorSetInfoVector::with_capacity(MAX_DESCRIPTOR_SET_COUNT); + /* + descriptors = { { 0, + { { { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0, + DescriptorFlags::NONE }, + {} } } } }; + */ + msl::collectDescriptors(config, descriptors); + msl::prettyPrintDescriptorSetInfoVector(descriptors); spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, - config.shaderModel, config.hasFramebufferFetch, sibs, + config.shaderModel, config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } } else { @@ -472,9 +795,11 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); -// msl::collectSibs(config, sibs); + auto descriptors = DescriptorSetInfoVector::with_capacity(MAX_DESCRIPTOR_SET_COUNT); + msl::collectDescriptors(config, descriptors); + msl::prettyPrintDescriptorSetInfoVector(descriptors); spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, - config.hasFramebufferFetch, sibs, + config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } @@ -513,9 +838,11 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); -// msl::collectSibs(config, sibs); + auto descriptors = DescriptorSetInfoVector::with_capacity(MAX_DESCRIPTOR_SET_COUNT); + msl::collectDescriptors(config, descriptors); + msl::prettyPrintDescriptorSetInfoVector(descriptors); spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, - sibs, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); + descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } // Transpile back to GLSL diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index c13dece6369..9bb2be5a352 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -20,6 +20,7 @@ #include // for MaterialBuilder:: enums #include +#include #include "ShaderMinifier.h" @@ -35,16 +36,20 @@ #include #include -namespace filament { -class SamplerInterfaceBlock; -}; - namespace filamat { using SpirvBlob = std::vector; using BindingPointAndSib = std::pair; using SibVector = utils::FixedCapacityVector; +using DescriptorInfo = std::tuple< + utils::CString, + filament::backend::DescriptorSetLayoutBinding, + std::optional>; +using DescriptorSetInfo = utils::FixedCapacityVector; +using BindingPointAndDescriptorSetInfo = std::pair; +using DescriptorSetInfoVector = utils::FixedCapacityVector; + class GLSLPostProcessor { public: enum Flags : uint32_t { @@ -58,6 +63,7 @@ class GLSLPostProcessor { struct Config { filament::Variant variant; + filament::UserVariantFilterMask variantFilter; MaterialBuilder::TargetApi targetApi; MaterialBuilder::TargetLanguage targetLanguage; filament::backend::ShaderStage shaderType; @@ -80,7 +86,7 @@ class GLSLPostProcessor { // public so backend_test can also use it static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, - const SibVector& sibs, const ShaderMinifier* minifier); + const DescriptorSetInfoVector& descriptorSets, const ShaderMinifier* minifier); private: struct InternalConfig { diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index b6b7404e6d8..5fe3dd303fd 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -937,6 +938,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector Date: Mon, 24 Jun 2024 15:16:12 -0700 Subject: [PATCH 08/49] Pass descriptor set info to spirvToMsl more concicely --- filament/backend/test/ShaderGenerator.cpp | 4 +- filament/backend/test/ShaderGenerator.h | 4 +- filament/backend/test/test_Blit.cpp | 9 ++--- filament/backend/test/test_BufferUpdates.cpp | 28 ++++++-------- filament/backend/test/test_FeedbackLoops.cpp | 10 ++--- filament/backend/test/test_LoadImage.cpp | 33 +++++++---------- filament/backend/test/test_MipLevels.cpp | 8 ++-- .../backend/test/test_RenderExternalImage.cpp | 16 +++----- libs/filamat/src/GLSLPostProcessor.cpp | 37 +++++++------------ libs/filamat/src/GLSLPostProcessor.h | 5 +-- 10 files changed, 62 insertions(+), 92 deletions(-) diff --git a/filament/backend/test/ShaderGenerator.cpp b/filament/backend/test/ShaderGenerator.cpp index d8e6939fade..d953f53d9f6 100644 --- a/filament/backend/test/ShaderGenerator.cpp +++ b/filament/backend/test/ShaderGenerator.cpp @@ -74,7 +74,7 @@ void ShaderGenerator::shutdown() { } ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, Backend backend, - bool isMobile, filamat::DescriptorSetInfoVector&& descriptorSets) noexcept + bool isMobile, filamat::DescriptorSets&& descriptorSets) noexcept : mBackend(backend), mVertexBlob(transpileShader( ShaderStage::VERTEX, std::move(vertex), backend, isMobile, descriptorSets)), @@ -97,7 +97,7 @@ ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, Backe } ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::string shader, - Backend backend, bool isMobile, const filamat::DescriptorSetInfoVector& descriptorSets) noexcept { + Backend backend, bool isMobile, const filamat::DescriptorSets& descriptorSets) noexcept { TProgram program; const EShLanguage language = stage == ShaderStage::VERTEX ? EShLangVertex : EShLangFragment; TShader tShader(language); diff --git a/filament/backend/test/ShaderGenerator.h b/filament/backend/test/ShaderGenerator.h index 31ed6c3d5a1..9ce6d76b3d2 100644 --- a/filament/backend/test/ShaderGenerator.h +++ b/filament/backend/test/ShaderGenerator.h @@ -41,7 +41,7 @@ class ShaderGenerator { * @param fragment The fragment shader, written in GLSL 450 core. */ ShaderGenerator(std::string vertex, std::string fragment, Backend backend, bool isMobile, - filamat::DescriptorSetInfoVector&& descriptorSets = {}) noexcept; + filamat::DescriptorSets&& descriptorSets = {}) noexcept; ShaderGenerator(const ShaderGenerator& rhs) = delete; ShaderGenerator& operator=(const ShaderGenerator& rhs) = delete; @@ -53,7 +53,7 @@ class ShaderGenerator { using Blob = std::vector; static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend, - bool isMobile, const filamat::DescriptorSetInfoVector& descriptorSets) noexcept; + bool isMobile, const filamat::DescriptorSets& descriptorSets) noexcept; Backend mBackend; diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index 3f342ff7ada..b71f263ba20 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -348,11 +348,10 @@ TEST_F(BackendTest, ColorResolve) { // Create a program. ProgramHandle program; { - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0}, {}} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "Params", + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0 }, + {} } }; ShaderGenerator shaderGen( triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors)); Program prog = shaderGen.getProgram(api); diff --git a/filament/backend/test/test_BufferUpdates.cpp b/filament/backend/test/test_BufferUpdates.cpp index 76ac2374e1c..96e1026d59b 100644 --- a/filament/backend/test/test_BufferUpdates.cpp +++ b/filament/backend/test/test_BufferUpdates.cpp @@ -31,7 +31,7 @@ layout(location = 0) in vec4 mesh_position; layout(location = 0) out uvec4 indices; -layout(binding = 0, set = 0) uniform Params { +layout(binding = 0, set = 1) uniform Params { highp vec4 padding[4]; // offset of 64 bytes highp vec4 color; @@ -51,7 +51,7 @@ std::string fragment (R"(#version 450 core layout(location = 0) out vec4 fragColor; -uniform Params { +layout(binding = 0, set = 1) uniform Params { highp vec4 padding[4]; // offset of 64 bytes highp vec4 color; @@ -96,15 +96,13 @@ TEST_F(BackendTest, VertexBufferUpdate) { api.makeCurrent(swapChain, swapChain); // Create a program. - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0}, {}} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "Params", + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } }; ShaderGenerator shaderGen( vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program p = shaderGen.getProgram(api); - p.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); + p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); auto program = api.createProgram(std::move(p)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ @@ -144,7 +142,7 @@ TEST_F(BackendTest, VertexBufferUpdate) { BufferObjectBinding::UNIFORM, BufferUsage::STATIC); api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); api.startCapture(0); @@ -226,15 +224,13 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { api.makeCurrent(swapChain, swapChain); // Create a program. - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0}, {}} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "Params", + { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } }; ShaderGenerator shaderGen( vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program p = shaderGen.getProgram(api); - p.descriptorBindings(0, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); + p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }}); auto program = api.createProgram(std::move(p)); DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ @@ -254,7 +250,7 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { BufferObjectBinding::UNIFORM, BufferUsage::STATIC); api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet, 1, {}); // Create a render target. auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1, diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index 1cc30049227..c483d92799b 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -142,11 +142,11 @@ TEST_F(BackendTest, FeedbackLoops) { { filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo}, - {"Params", {DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 1}, {}} - }} + filamat::DescriptorSets descriptors; + descriptors[1] = { + { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo }, + { "Params", { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 1 }, {} } }; ShaderGenerator shaderGen(fullscreenVs, fullscreenFs, sBackend, sIsMobilePlatform, std::move(descriptors)); diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index fa2f15b1180..cf140b065b2 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -303,11 +303,9 @@ TEST_F(BackendTest, UpdateImage2D) { // Create a program. filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo } }; std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(t.textureFormat), fragmentTemplate); @@ -391,11 +389,10 @@ TEST_F(BackendTest, UpdateImageSRGB) { // Create a program. filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo } }; + std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentTemplate); ShaderGenerator shaderGen( @@ -487,11 +484,9 @@ TEST_F(BackendTest, UpdateImageMipLevel) { // Create a program. filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo } }; std::string const fragment = stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragmentUpdateImageMip); ShaderGenerator shaderGen( @@ -569,11 +564,9 @@ TEST_F(BackendTest, UpdateImage3D) { // Create a program. filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo } }; std::string fragment = stringReplace("{samplerType}", getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate); ShaderGenerator shaderGen( diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp index cb15e1923fd..94944f797ec 100644 --- a/filament/backend/test/test_MipLevels.cpp +++ b/filament/backend/test/test_MipLevels.cpp @@ -100,11 +100,9 @@ TEST_F(BackendTest, SetMinMaxLevel) { { filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "backend_test", "sib_tex", 0, SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"backend_test_sib_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "backend_test_sib_tex", + { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, samplerInfo } }; ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); Program p = shaderGen.getProgram(api); p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}}); diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index bbc61f44885..08e94c737da 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -73,11 +73,9 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo } }; ShaderGenerator shaderGen( vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); @@ -163,11 +161,9 @@ TEST_F(BackendTest, RenderExternalImage) { filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0, SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false }; - filamat::DescriptorSetInfoVector descriptors = { - {1, { - {"test_tex", {DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0}, samplerInfo} - }} - }; + filamat::DescriptorSets descriptors; + descriptors[1] = { { "test_tex", { DescriptorType::SAMPLER, ShaderStageFlags::FRAGMENT, 0 }, + samplerInfo } }; ShaderGenerator shaderGen( vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors)); diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index ab74cfc0768..58d2a692b2b 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -266,7 +266,7 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set, } } -void prettyPrintDescriptorSetInfoVector(DescriptorSetInfoVector const& sets) { +void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) { auto getName = [](uint8_t set) { switch (set) { case +DescriptorSetBindingPoints::PER_VIEW: @@ -279,8 +279,8 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSetInfoVector const& sets) { return "unknown"; } }; - for (auto const& set : sets) { - auto const& [setIndex, descriptors] = set; + for (size_t setIndex = 0; setIndex < MAX_DESCRIPTOR_SET_COUNT; setIndex++) { + auto const& descriptors = sets[setIndex]; printf("[DS] info (%s) = [\n", getName(setIndex)); for (auto const& descriptor : descriptors) { auto const& [name, info, sampler] = descriptor; @@ -305,22 +305,20 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSetInfoVector const& sets) { } // TODO: maybe rename to collectDescriptorSets -static void collectDescriptors( - const GLSLPostProcessor::Config& config, DescriptorSetInfoVector& sets) { +static void collectDescriptors(const GLSLPostProcessor::Config& config, DescriptorSets& sets) { auto perViewDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); collectDescriptorsForSet(DescriptorSetBindingPoints::PER_VIEW, config, perViewDescriptors); - sets.emplace_back(+DescriptorSetBindingPoints::PER_VIEW, std::move(perViewDescriptors)); + sets[+DescriptorSetBindingPoints::PER_VIEW] = std::move(perViewDescriptors); auto perRenderableDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); collectDescriptorsForSet( DescriptorSetBindingPoints::PER_RENDERABLE, config, perRenderableDescriptors); - sets.emplace_back( - +DescriptorSetBindingPoints::PER_RENDERABLE, std::move(perRenderableDescriptors)); + sets[+DescriptorSetBindingPoints::PER_RENDERABLE] = std::move(perRenderableDescriptors); auto perMaterialDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); collectDescriptorsForSet( DescriptorSetBindingPoints::PER_MATERIAL, config, perMaterialDescriptors); - sets.emplace_back(+DescriptorSetBindingPoints::PER_MATERIAL, std::move(perMaterialDescriptors)); + sets[+DescriptorSetBindingPoints::PER_MATERIAL] = std::move(perMaterialDescriptors); } } // namespace msl @@ -386,7 +384,7 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, - const DescriptorSetInfoVector& descriptorSets, const ShaderMinifier* minifier) { + const DescriptorSets& descriptorSets, const ShaderMinifier* minifier) { using namespace msl; CompilerMSL mslCompiler(*spirv); @@ -548,8 +546,8 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, // with our own that contain all the descriptors, even those optimized away. std::vector argumentBuffers; size_t dynamicOffsetsBufferIndex = 0; - for (auto const& set : descriptorSets) { - auto const& [setIndex, descriptors] = set; + for (size_t setIndex = 0; setIndex < MAX_DESCRIPTOR_SET_COUNT; setIndex++) { + auto const& descriptors = descriptorSets[setIndex]; auto argBufferBuilder = MetalArgumentBuffer::Builder().name( "spvDescriptorSetBuffer" + std::to_string(int(setIndex))); for (auto const& descriptor : descriptors) { @@ -697,14 +695,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co fixupClipDistance(*internalConfig.spirvOutput, config); if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); - auto descriptors = - DescriptorSetInfoVector::with_capacity(MAX_DESCRIPTOR_SET_COUNT); - /* - descriptors = { { 0, - { { { DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0, - DescriptorFlags::NONE }, - {} } } } }; - */ + DescriptorSets descriptors {}; msl::collectDescriptors(config, descriptors); msl::prettyPrintDescriptorSetInfoVector(descriptors); spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, @@ -794,8 +785,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, } if (internalConfig.mslOutput) { - auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); - auto descriptors = DescriptorSetInfoVector::with_capacity(MAX_DESCRIPTOR_SET_COUNT); + DescriptorSets descriptors {}; msl::collectDescriptors(config, descriptors); msl::prettyPrintDescriptorSetInfoVector(descriptors); spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, @@ -837,8 +827,7 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, } if (internalConfig.mslOutput) { - auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); - auto descriptors = DescriptorSetInfoVector::with_capacity(MAX_DESCRIPTOR_SET_COUNT); + DescriptorSets descriptors {}; msl::collectDescriptors(config, descriptors); msl::prettyPrintDescriptorSetInfoVector(descriptors); spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 9bb2be5a352..e13f1a5846b 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -47,8 +47,7 @@ using DescriptorInfo = std::tuple< filament::backend::DescriptorSetLayoutBinding, std::optional>; using DescriptorSetInfo = utils::FixedCapacityVector; -using BindingPointAndDescriptorSetInfo = std::pair; -using DescriptorSetInfoVector = utils::FixedCapacityVector; +using DescriptorSets = std::array; class GLSLPostProcessor { public: @@ -86,7 +85,7 @@ class GLSLPostProcessor { // public so backend_test can also use it static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, - const DescriptorSetInfoVector& descriptorSets, const ShaderMinifier* minifier); + const DescriptorSets& descriptorSets, const ShaderMinifier* minifier); private: struct InternalConfig { From a0dc7104b67ed67edbdc6dce2ff208f2da7130b7 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 24 Jun 2024 22:07:24 -0700 Subject: [PATCH 09/49] fix backend tests The pipeline layout wasn't set properly --- filament/backend/test/test_Blit.cpp | 2 +- filament/backend/test/test_BufferUpdates.cpp | 6 +++--- filament/backend/test/test_FeedbackLoops.cpp | 2 +- filament/backend/test/test_LoadImage.cpp | 12 ++++++++---- filament/backend/test/test_RenderExternalImage.cpp | 4 ++-- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index b71f263ba20..8ba0be44c94 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -402,7 +402,7 @@ TEST_F(BackendTest, ColorResolve) { PipelineState state = {}; state.program = program; - state.pipelineLayout.setLayout = { descriptorSetLayout }; + state.pipelineLayout.setLayout[1] = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; diff --git a/filament/backend/test/test_BufferUpdates.cpp b/filament/backend/test/test_BufferUpdates.cpp index 96e1026d59b..bcaa2fc21a4 100644 --- a/filament/backend/test/test_BufferUpdates.cpp +++ b/filament/backend/test/test_BufferUpdates.cpp @@ -129,7 +129,7 @@ TEST_F(BackendTest, VertexBufferUpdate) { PipelineState state; state.program = program; - state.pipelineLayout.setLayout = { descriptorSetLayout }; + state.pipelineLayout.setLayout[1] = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; @@ -280,7 +280,7 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { params.flags.discardEnd = TargetBufferFlags::NONE; params.viewport.height = 512; params.viewport.width = 512; - renderTriangle({ descriptorSetLayout }, + renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }}, renderTarget, swapChain, program, params); // Upload uniforms for the second triangle. To test partial buffer updates, we'll only update @@ -301,7 +301,7 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) { params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; - renderTriangle({ descriptorSetLayout }, + renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }}, renderTarget, swapChain, program, params); static const uint32_t expectedHash = 91322442; diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index c483d92799b..8d49ef2acd5 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -225,7 +225,7 @@ TEST_F(BackendTest, FeedbackLoops) { state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; state.program = program; - state.pipelineLayout.setLayout = { descriptorSetLayout }; + state.pipelineLayout.setLayout[1] = { descriptorSetLayout }; api.makeCurrent(swapChain, swapChain); api.beginFrame(0, 0, 0); diff --git a/filament/backend/test/test_LoadImage.cpp b/filament/backend/test/test_LoadImage.cpp index cf140b065b2..c5946cd2293 100644 --- a/filament/backend/test/test_LoadImage.cpp +++ b/filament/backend/test/test_LoadImage.cpp @@ -352,7 +352,8 @@ TEST_F(BackendTest, UpdateImage2D) { api.bindDescriptorSet(descriptorSet, 1, {}); - renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); + renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }}, + defaultRenderTarget, swapChain, program); readPixelsAndAssertHash(t.name, 512, 512, defaultRenderTarget, expectedHash); @@ -446,7 +447,8 @@ TEST_F(BackendTest, UpdateImageSRGB) { api.bindDescriptorSet(descriptorSet, 1, {}); - renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); + renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }}, + defaultRenderTarget, swapChain, program); static const uint32_t expectedHash = 359858623; readPixelsAndAssertHash("UpdateImageSRGB", 512, 512, defaultRenderTarget, expectedHash); @@ -524,7 +526,8 @@ TEST_F(BackendTest, UpdateImageMipLevel) { api.bindDescriptorSet(descriptorSet, 1, {}); - renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); + renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }}, + defaultRenderTarget, swapChain, program); static const uint32_t expectedHash = 3644679986; readPixelsAndAssertHash("UpdateImageMipLevel", 512, 512, defaultRenderTarget, expectedHash); @@ -614,7 +617,8 @@ TEST_F(BackendTest, UpdateImage3D) { api.bindDescriptorSet(descriptorSet, 1, {}); - renderTriangle({ descriptorSetLayout }, defaultRenderTarget, swapChain, program); + renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }}, + defaultRenderTarget, swapChain, program); static const uint32_t expectedHash = 3644679986; readPixelsAndAssertHash("UpdateImage3D", 512, 512, defaultRenderTarget, expectedHash); diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index 08e94c737da..57e138dea16 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -116,7 +116,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { PipelineState state; state.program = program; - state.pipelineLayout.setLayout = { descriptorSetLayout }; + state.pipelineLayout.setLayout[1] = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; @@ -247,7 +247,7 @@ TEST_F(BackendTest, RenderExternalImage) { PipelineState state; state.program = program; - state.pipelineLayout.setLayout = { descriptorSetLayout }; + state.pipelineLayout.setLayout[1] = { descriptorSetLayout }; state.rasterState.colorWrite = true; state.rasterState.depthWrite = false; state.rasterState.depthFunc = RasterState::DepthFunc::A; From b4162eb313f3a5b60a618e9327f37a51dd45cf1a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 24 Jun 2024 22:57:09 -0700 Subject: [PATCH 10/49] fix windows build --- libs/filamat/src/GLSLPostProcessor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index e13f1a5846b..b873a383ca1 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -33,6 +33,7 @@ #include #include +#include #include #include From 07f07e0700241659060a4ee56d4b165346384eca Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Thu, 27 Jun 2024 14:35:20 -0700 Subject: [PATCH 11/49] Remove log spew --- filament/backend/src/metal/MetalDriver.mm | 83 +++++++++++++---------- filament/backend/src/metal/MetalHandles.h | 10 --- filament/src/DFG.cpp | 2 - filament/src/details/Texture.cpp | 2 - libs/filamat/src/GLSLPostProcessor.cpp | 10 +++ 5 files changed, 59 insertions(+), 48 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index ba7c3041282..5c87ebae978 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -41,6 +41,16 @@ #include +#ifndef DEBUG_LOG_DESCRIPTOR_SETS +#define DEBUG_LOG_DESCRIPTOR_SETS 0 +#endif + +#if DEBUG_LOG_DESCRIPTOR_SETS == 1 +#define DEBUG_LOG(...) printf(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif + namespace filament { namespace backend { @@ -227,7 +237,8 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, int64_t refreshIntervalNs, uint32_t frameId) { - printf("[DS] beginFrame(monotonic_clock_ns = %lld, refreshIntervalNs = %lld, frameId = %d)\n", + DEBUG_LOG( + "[DS] beginFrame(monotonic_clock_ns = %lld, refreshIntervalNs = %lld, frameId = %d)\n", monotonic_clock_ns, refreshIntervalNs, frameId); #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId); @@ -265,7 +276,7 @@ } void MetalDriver::endFrame(uint32_t frameId) { - printf("[DS] endFrame(frameId = %d)\n", frameId); + DEBUG_LOG("[DS] endFrame(frameId = %d)\n", frameId); // If we haven't committed the command buffer (if the frame was canceled), do it now. There may // be commands in it (like fence signaling) that need to execute. submitPendingCommands(mContext); @@ -306,8 +317,9 @@ uint32_t size) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "updateDescriptorSetBuffer must be called outside of a render pass."); - printf("[DS] updateDescriptorSetBuffer(dsh = %d, binding = %d, boh = %d, offset = %d, size = " - "%d)\n", + DEBUG_LOG( + "[DS] updateDescriptorSetBuffer(dsh = %d, binding = %d, boh = %d, offset = %d, size = " + "%d)\n", dsh.getId(), binding, boh.getId(), offset, size); auto* descriptorSet = handle_cast(dsh); @@ -321,10 +333,9 @@ SamplerParams params) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "updateDescriptorSetTexture must be called outside of a render pass."); - printf("[DS] updateDescriptorSetTexture(dsh = %d, binding = %d, th = %d, params = {...})\n", + DEBUG_LOG("[DS] updateDescriptorSetTexture(dsh = %d, binding = %d, th = %d, params = {...})\n", dsh.getId(), binding, th.getId()); - auto* descriptorSet = handle_cast(dsh); descriptorSet->textures[binding] = MetalDescriptorSet::TextureBinding{ th, params }; } @@ -380,8 +391,9 @@ TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3)); - printf("createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = " - "%d, height = %d, depth = %d, usage = %s)\n", + DEBUG_LOG( + "createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = " + "%d, height = %d, depth = %d, usage = %s)\n", th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); } @@ -396,8 +408,9 @@ mContext->textures.insert(construct_handle(th, *mContext, target, levels, format, samples, width, height, depth, usage, r, g, b, a)); - printf("createTextureSwizzledR(th = %d, target = %s, levels = %d, format = ?, samples = %d, " - "width = %d, height = %d, depth = %d, usage = %s, r = ?, g = ?, b = ?, a = ?)\n", + DEBUG_LOG( + "createTextureSwizzledR(th = %d, target = %s, levels = %d, format = ?, samples = %d, " + "width = %d, height = %d, depth = %d, usage = %s, r = ?, g = ?, b = ?, a = ?)\n", th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); } @@ -559,25 +572,25 @@ return "NONE"; } -void MetalDriver::createDescriptorSetLayoutR(Handle dslh, - DescriptorSetLayout&& info) { - printf("[DS] createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); +void MetalDriver::createDescriptorSetLayoutR( + Handle dslh, DescriptorSetLayout&& info) { + DEBUG_LOG("[DS] createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); for (size_t i = 0; i < info.bindings.size(); i++) { - printf(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s}", + DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s}", info.bindings[i].binding, prettyDescriptorType(info.bindings[i].type), info.bindings[i].count, prettyShaderStageFlags(info.bindings[i].stageFlags), prettyDescriptorFlags(info.bindings[i].flags)); - printf(",\n"); + DEBUG_LOG(",\n"); } - printf("})\n"); + DEBUG_LOG("})\n"); std::sort(info.bindings.begin(), info.bindings.end(), [](const auto& a, const auto& b) { return a.binding < b.binding; }); construct_handle(dslh, std::move(info)); } -void MetalDriver::createDescriptorSetR(Handle dsh, - Handle dslh) { - printf("[DS] createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId()); +void MetalDriver::createDescriptorSetR( + Handle dsh, Handle dslh) { + DEBUG_LOG("[DS] createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId()); MetalDescriptorSetLayout* layout = handle_cast(dslh); construct_handle(dsh, layout); } @@ -768,14 +781,14 @@ } void MetalDriver::destroyDescriptorSetLayout(Handle dslh) { - printf("[DS] destroyDescriptorSetLayout(dslh = %d)\n", dslh.getId()); + DEBUG_LOG("[DS] destroyDescriptorSetLayout(dslh = %d)\n", dslh.getId()); if (dslh) { destruct_handle(dslh); } } void MetalDriver::destroyDescriptorSet(Handle dsh) { - printf("[DS] destroyDescriptorSet(dsh = %d)\n", dsh.getId()); + DEBUG_LOG("[DS] destroyDescriptorSet(dsh = %d)\n", dsh.getId()); if (dsh) { destruct_handle(dsh); } @@ -1051,7 +1064,7 @@ auto tex = handle_cast(th); tex->setLodRange(minLevel, maxLevel); - printf("setMinMaxLevels(th = %d, minLevel = %d, maxLevel = %d)\n", th.getId(), minLevel, + DEBUG_LOG("setMinMaxLevels(th = %d, minLevel = %d, maxLevel = %d)\n", th.getId(), minLevel, maxLevel); } @@ -1065,8 +1078,9 @@ tex->loadImage(level, MTLRegionMake3D(xoffset, yoffset, zoffset, width, height, depth), data); scheduleDestroy(std::move(data)); - printf("update3DImage(th = %d, level = %d, xoffset = %d, yoffset = %d, zoffset = %d, width = " - "%d, height = %d, depth = %d, data = ?)\n", + DEBUG_LOG( + "update3DImage(th = %d, level = %d, xoffset = %d, yoffset = %d, zoffset = %d, width = " + "%d, height = %d, depth = %d, data = ?)\n", th.getId(), level, xoffset, yoffset, zoffset, width, height, depth); } @@ -1108,7 +1122,7 @@ auto tex = handle_cast(th); tex->generateMipmaps(); - printf("generateMipmaps(th = %d)\n", th.getId()); + DEBUG_LOG("generateMipmaps(th = %d)\n", th.getId()); } void MetalDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { @@ -1210,7 +1224,7 @@ void MetalDriver::beginRenderPass(Handle rth, const RenderPassParams& params) { - printf("[DS] beginRenderPass(rth = %d, params = {...})\n", rth.getId()); + DEBUG_LOG("[DS] beginRenderPass(rth = %d, params = {...})\n", rth.getId()); #if defined(FILAMENT_METAL_PROFILING) const char* renderPassName = "Unknown"; @@ -1265,7 +1279,7 @@ void MetalDriver::nextSubpass(int dummy) {} void MetalDriver::endRenderPass(int dummy) { - printf("[DS] endRenderPass()\n"); + DEBUG_LOG("[DS] endRenderPass()\n"); #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_end(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass"); #endif @@ -1614,8 +1628,9 @@ dstTexture->extendLodRangeTo(dstLevel); - printf("blit(dst = %d, srcLevel = %d, srcLayer = %d, dstOrigin = (%d, %d), src = %d, dstLevel " - "= %d, dstLayer = %d, srcOrigin = (%d, %d), size = (%d, %d))\n", + DEBUG_LOG( + "blit(dst = %d, srcLevel = %d, srcLayer = %d, dstOrigin = (%d, %d), src = %d, dstLevel " + "= %d, dstLayer = %d, srcOrigin = (%d, %d), size = (%d, %d))\n", dst.getId(), srcLevel, srcLayer, dstOrigin.x, dstOrigin.y, src.getId(), dstLevel, dstLayer, srcOrigin.x, srcOrigin.y, size.x, size.y); } @@ -1983,16 +1998,16 @@ auto descriptorSet = handle_cast(dsh); const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount(); utils::FixedCapacityVector offsetsVector(dynamicBindings, 0); - printf("[DS] bindDescriptorSet(dsh = %d, set = %d, offsets = [", dsh.getId(), set); + DEBUG_LOG("[DS] bindDescriptorSet(dsh = %d, set = %d, offsets = [", dsh.getId(), set); for (size_t i = 0; i < dynamicBindings; i++) { - printf("%d", offsets[i]); + DEBUG_LOG("%d", offsets[i]); if (i < dynamicBindings - 1) { - printf(", "); + DEBUG_LOG(", "); } offsetsVector[i] = offsets[i]; } - printf("])\n"); + DEBUG_LOG("])\n"); // Bind the descriptor set. mContext->currentDescriptorSets[set] = descriptorSet; @@ -2003,7 +2018,7 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) << "draw() without a valid command encoder."; - printf("[DS] draw2(...)\n"); + DEBUG_LOG("[DS] draw2(...)\n"); // Finalize any descriptor sets that have been bound. for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 0461cb1259d..7a506e023df 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -642,24 +642,19 @@ struct MetalDescriptorSet : public HwDescriptorSet { options:MTLResourceStorageModeShared]; [encoder setArgumentBuffer:buffer offset:0]; - printf("finalizeAndGetBuffer -- encoding begin --\n"); for (auto const& binding : bindings) { - printf(" binding %d: ", binding.binding); switch (binding.type) { case DescriptorType::UNIFORM_BUFFER: case DescriptorType::SHADER_STORAGE_BUFFER: { - printf("uniform, "); auto found = buffers.find(binding.binding); if (found == buffers.end()) { [encoder setBuffer:driver->mContext->emptyBuffer offset:0 atIndex:binding.binding * 2]; - printf("empty buffer\n"); continue; } auto const& bufferBinding = buffers[binding.binding]; - printf("buffer handle: %d", bufferBinding.buffer.getId()); auto* metalBuffer = driver->handle_cast(bufferBinding.buffer) ->getBuffer() ->getGpuBufferForDraw(); @@ -669,7 +664,6 @@ struct MetalDescriptorSet : public HwDescriptorSet { break; } case DescriptorType::SAMPLER: { - printf("sampler, "); auto found = textures.find(binding.binding); if (found == textures.end()) { [encoder setTexture:driver->mContext->emptyTexture @@ -678,12 +672,10 @@ struct MetalDescriptorSet : public HwDescriptorSet { driver->mContext->samplerStateCache.getOrCreateState({}); [encoder setSamplerState:sampler atIndex:binding.binding * 2 + 1]; - printf("empty texture\n"); continue; } auto const& textureBinding = textures[binding.binding]; - printf("texture handle: %d", textureBinding.texture.getId()); auto* texture = driver->handle_cast(textureBinding.texture) ->getMtlTextureForRead(); [encoder setTexture:texture atIndex:binding.binding * 2]; @@ -699,9 +691,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { assert_invariant(false); break; } - printf("\n"); } - printf("finalizeAndGetBuffer -- end --\n"); return buffer; } diff --git a/filament/src/DFG.cpp b/filament/src/DFG.cpp index 64d18d9bd6a..933d97d525b 100644 --- a/filament/src/DFG.cpp +++ b/filament/src/DFG.cpp @@ -31,13 +31,11 @@ void DFG::init(FEngine& engine) noexcept { static_assert(sizeof(DFG_LUT) == byteCount, "DFG_LUT_SIZE doesn't match size of the DFG LUT"); - printf("DFG LUT --begin--:\n"); Texture* lut = Texture::Builder() .width(DFG_LUT_SIZE) .height(DFG_LUT_SIZE) .format(backend::TextureFormat::RGB16F) .build(engine); - printf("DFG LUT --end--:\n"); Texture::PixelBufferDescriptor buffer(DFG_LUT, byteCount, Texture::Format::RGB, Texture::Type::HALF); diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 4994cf14a24..3734df4b6d9 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -243,8 +243,6 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { mHandle = driver.importTexture(builder->mImportedId, mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage); } - - printf("FTexture::FTexture, mHandle = %d\n", mHandle.getId()); } // frees driver resources, object becomes invalid diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 58d2a692b2b..6853c911a42 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -117,6 +117,10 @@ using BindingIndexMap = std::unordered_map; // &config.materialInfo->sib); //} +#ifndef DEBUG_LOG_DESCRIPTOR_SETS +#define DEBUG_LOG_DESCRIPTOR_SETS 0 +#endif + const char* prettyDescriptorType(DescriptorType type) { switch (type) { case DescriptorType::UNIFORM_BUFFER: return "UNIFORM_BUFFER"; @@ -697,7 +701,9 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); DescriptorSets descriptors {}; msl::collectDescriptors(config, descriptors); +#if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); +#endif spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); @@ -787,7 +793,9 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, if (internalConfig.mslOutput) { DescriptorSets descriptors {}; msl::collectDescriptors(config, descriptors); +#if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); +#endif spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); @@ -829,7 +837,9 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, if (internalConfig.mslOutput) { DescriptorSets descriptors {}; msl::collectDescriptors(config, descriptors); +#if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); +#endif spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } From c12bcbb21e8e916cc87315f7a3a56d733572aacb Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 25 Jun 2024 10:31:06 -0700 Subject: [PATCH 12/49] Bind the actual resource at updateDescriptorSet time --- filament/backend/src/metal/MetalDriver.mm | 7 ++++-- filament/backend/src/metal/MetalHandles.h | 26 ++++++----------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 5c87ebae978..ce88592a275 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -323,7 +323,8 @@ dsh.getId(), binding, boh.getId(), offset, size); auto* descriptorSet = handle_cast(dsh); - descriptorSet->buffers[binding] = { boh, offset, size }; + auto* bo = handle_cast(boh); + descriptorSet->buffers[binding] = { bo->getBuffer()->getGpuBufferForDraw(), offset, size }; } void MetalDriver::updateDescriptorSetTexture( @@ -337,7 +338,9 @@ dsh.getId(), binding, th.getId()); auto* descriptorSet = handle_cast(dsh); - descriptorSet->textures[binding] = MetalDescriptorSet::TextureBinding{ th, params }; + auto* texture = handle_cast(th); + descriptorSet->textures[binding] = + MetalDescriptorSet::TextureBinding { texture->getMtlTextureForRead(), params }; } void MetalDriver::flush(int) { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 7a506e023df..61faf9f98ad 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -588,10 +588,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { } auto const& bufferBinding = buffers[binding.binding]; - auto* metalBuffer = driver->handle_cast(bufferBinding.buffer) - ->getBuffer() - ->getGpuBufferForDraw(); - [driver->mContext->currentRenderPassEncoder useResource:metalBuffer + [driver->mContext->currentRenderPassEncoder useResource:bufferBinding.buffer usage:MTLResourceUsageRead]; break; } @@ -602,9 +599,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { } auto const& textureBinding = textures[binding.binding]; - auto* texture = driver->handle_cast(textureBinding.texture) - ->getMtlTextureForRead(); - [driver->mContext->currentRenderPassEncoder useResource:texture + [driver->mContext->currentRenderPassEncoder useResource:textureBinding.texture usage:MTLResourceUsageRead]; break; } @@ -626,9 +621,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { MTLTextureType textureType = MTLTextureType2D; if (auto found = textures.find(binding.binding); found != textures.end()) { auto const& textureBinding = textures[binding.binding]; - auto* texture = driver->handle_cast(textureBinding.texture) - ->getMtlTextureForRead(); - textureType = texture.textureType; + textureType = textureBinding.texture.textureType; } textureTypes.push_back(textureType); } @@ -655,10 +648,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { } auto const& bufferBinding = buffers[binding.binding]; - auto* metalBuffer = driver->handle_cast(bufferBinding.buffer) - ->getBuffer() - ->getGpuBufferForDraw(); - [encoder setBuffer:metalBuffer + [encoder setBuffer:bufferBinding.buffer offset:bufferBinding.offset atIndex:binding.binding * 2]; break; @@ -676,9 +666,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { } auto const& textureBinding = textures[binding.binding]; - auto* texture = driver->handle_cast(textureBinding.texture) - ->getMtlTextureForRead(); - [encoder setTexture:texture atIndex:binding.binding * 2]; + [encoder setTexture:textureBinding.texture atIndex:binding.binding * 2]; SamplerState samplerState { .samplerParams = textureBinding.sampler }; @@ -697,12 +685,12 @@ struct MetalDescriptorSet : public HwDescriptorSet { } struct BufferBinding { - BufferObjectHandle buffer; + id buffer; uint32_t offset; uint32_t size; }; struct TextureBinding { - TextureHandle texture; + id texture; SamplerParams sampler; }; tsl::robin_map buffers; From 929ad276067cd2e51fead724ede77fef5d7a3c4d Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 25 Jun 2024 10:49:02 -0700 Subject: [PATCH 13/49] Pass the correct stage flags --- filament/backend/src/metal/MetalDriver.mm | 27 +++++++++-- filament/backend/src/metal/MetalHandles.h | 58 ++++++++++------------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index ce88592a275..e214f994a9b 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -324,7 +324,15 @@ auto* descriptorSet = handle_cast(dsh); auto* bo = handle_cast(boh); - descriptorSet->buffers[binding] = { bo->getBuffer()->getGpuBufferForDraw(), offset, size }; + id mtlBuffer = bo->getBuffer()->getGpuBufferForDraw(); + descriptorSet->buffers[binding] = { mtlBuffer, offset, size }; + ShaderStageFlags stageFlags = descriptorSet->layout->getBindings()[binding].stageFlags; + if (any(stageFlags & ShaderStageFlags::VERTEX)) { + descriptorSet->vertexResources.push_back(mtlBuffer); + } + if (any(stageFlags & ShaderStageFlags::FRAGMENT)) { + descriptorSet->fragmentResources.push_back(mtlBuffer); + } } void MetalDriver::updateDescriptorSetTexture( @@ -339,8 +347,21 @@ auto* descriptorSet = handle_cast(dsh); auto* texture = handle_cast(th); - descriptorSet->textures[binding] = - MetalDescriptorSet::TextureBinding { texture->getMtlTextureForRead(), params }; + id mtlTexture = texture->getMtlTextureForRead(); + descriptorSet->textures[binding] = MetalDescriptorSet::TextureBinding { mtlTexture, params }; + + auto const& bindings = descriptorSet->layout->getBindings(); + auto found = std::find_if(bindings.begin(), bindings.end(), + [binding](const auto& b) { return b.binding == binding; }); + assert_invariant(found != bindings.end()); + + ShaderStageFlags stageFlags = found->stageFlags; + if (any(stageFlags & ShaderStageFlags::VERTEX)) { + descriptorSet->vertexResources.push_back(mtlTexture); + } + if (any(stageFlags & ShaderStageFlags::FRAGMENT)) { + descriptorSet->fragmentResources.push_back(mtlTexture); + } } void MetalDriver::flush(int) { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 61faf9f98ad..f723a37aeb5 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -44,6 +44,7 @@ #include #include #include +#include namespace filament { namespace backend { @@ -570,42 +571,30 @@ class MetalDescriptorSetLayout : public HwDescriptorSetLayout { struct MetalDescriptorSet : public HwDescriptorSet { MetalDescriptorSet(MetalDescriptorSetLayout* layout) noexcept; - MetalDescriptorSetLayout* layout; void finalize(MetalDriver* driver) { [driver->mContext->currentRenderPassEncoder useResource:driver->mContext->emptyBuffer usage:MTLResourceUsageRead]; - [driver->mContext->currentRenderPassEncoder useResource:getOrCreateEmptyTexture(driver->mContext) - usage:MTLResourceUsageRead]; - auto const& bindings = this->layout->getBindings(); - for (auto const& binding : bindings) { - switch (binding.type) { - case DescriptorType::UNIFORM_BUFFER: - case DescriptorType::SHADER_STORAGE_BUFFER: { - auto found = buffers.find(binding.binding); - if (found == buffers.end()) { - continue; - } - - auto const& bufferBinding = buffers[binding.binding]; - [driver->mContext->currentRenderPassEncoder useResource:bufferBinding.buffer - usage:MTLResourceUsageRead]; - break; - } - case DescriptorType::SAMPLER: { - auto found = textures.find(binding.binding); - if (found == textures.end()) { - continue; - } - - auto const& textureBinding = textures[binding.binding]; - [driver->mContext->currentRenderPassEncoder useResource:textureBinding.texture - usage:MTLResourceUsageRead]; - break; - } - case DescriptorType::INPUT_ATTACHMENT: - break; - } + [driver->mContext->currentRenderPassEncoder + useResource:getOrCreateEmptyTexture(driver->mContext) + usage:MTLResourceUsageRead]; + + if (@available(iOS 13.0, *)) { + [driver->mContext->currentRenderPassEncoder useResources:vertexResources.data() + count:vertexResources.size() + usage:MTLResourceUsageRead + stages:MTLRenderStageVertex]; + [driver->mContext->currentRenderPassEncoder useResources:fragmentResources.data() + count:fragmentResources.size() + usage:MTLResourceUsageRead + stages:MTLRenderStageFragment]; + } else { + [driver->mContext->currentRenderPassEncoder useResources:vertexResources.data() + count:vertexResources.size() + usage:MTLResourceUsageRead]; + [driver->mContext->currentRenderPassEncoder useResources:fragmentResources.data() + count:fragmentResources.size() + usage:MTLResourceUsageRead]; } } @@ -684,6 +673,8 @@ struct MetalDescriptorSet : public HwDescriptorSet { return buffer; } + MetalDescriptorSetLayout* layout; + struct BufferBinding { id buffer; uint32_t offset; @@ -696,6 +687,9 @@ struct MetalDescriptorSet : public HwDescriptorSet { tsl::robin_map buffers; tsl::robin_map textures; + std::vector> vertexResources; + std::vector> fragmentResources; + id buffer = nil; }; From 38b307483567a12ba942abc899827e607565ecaa Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Thu, 27 Jun 2024 17:42:24 -0700 Subject: [PATCH 14/49] Fix validation error, bind descriptor sets at bind time --- filament/backend/src/metal/MetalDriver.mm | 36 ++++++++++++++--------- filament/backend/src/metal/MetalState.h | 14 +++++---- libs/utils/include/utils/bitset.h | 4 --- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index e214f994a9b..e97aebbcda4 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1295,6 +1295,19 @@ mContext->descriptorBindings.invalidate(); mContext->dynamicOffsets.setDirty(true); + // Finalize any descriptor sets that were bound before the render pass. + for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { + auto* descriptorSet = mContext->currentDescriptorSets[i]; + if (!descriptorSet) { + continue; + } + descriptorSet->finalize(this); + mContext->finalizedDescriptorSets.insert(descriptorSet); + } + + // Bind descriptor sets. + mContext->descriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + for (auto& pc : mContext->currentPushConstants) { pc.clear(); } @@ -2037,28 +2050,23 @@ mContext->currentDescriptorSets[set] = descriptorSet; mContext->descriptorBindings.setBuffer(descriptorSet->finalizeAndGetBuffer(this), 0, set); mContext->dynamicOffsets.setOffsets(set, offsets.data(), dynamicBindings); -} - -void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { - FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) - << "draw() without a valid command encoder."; - DEBUG_LOG("[DS] draw2(...)\n"); - // Finalize any descriptor sets that have been bound. - for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { - auto* descriptorSet = mContext->currentDescriptorSets[i]; - if (!descriptorSet) { - continue; - } + // If we're inside a render pass, we should also finalize the descriptor set and update the + // argument buffers. Otherwise, we'll do this the next time we enter a render pass. + if (isInRenderPass(mContext)) { auto found = mContext->finalizedDescriptorSets.find(descriptorSet); if (found == mContext->finalizedDescriptorSets.end()) { descriptorSet->finalize(this); mContext->finalizedDescriptorSets.insert(descriptorSet); } + mContext->descriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); } +} - // Bind descriptor sets. - mContext->descriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); +void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { + FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) + << "draw() without a valid command encoder."; + DEBUG_LOG("[DS] draw2(...)\n"); // Bind the offset data. if (mContext->dynamicOffsets.isDirty()) { diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index d8edaa8a37a..0f604e264bf 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -418,11 +418,15 @@ using ArgumentEncoderCache = StateCache class MetalBufferBindings { public: - MetalBufferBindings() {} + MetalBufferBindings() { invalidate(); } void invalidate() { - mDirtyBuffers.allOn(); - mDirtyOffsets.allOn(); + mDirtyBuffers.reset(); + mDirtyOffsets.reset(); + for (int i = 0; i < int(N); i++) { + mDirtyBuffers.set(i, true); + mDirtyOffsets.set(i, true); + } } void setBuffer(const id buffer, NSUInteger offset, NSUInteger index); void bindBuffers(id encoder, NSUInteger startIndex); @@ -431,8 +435,8 @@ class MetalBufferBindings { static_assert(N <= 8); std::array<__unsafe_unretained id, N> mBuffers = { nil }; std::array mOffsets = { 0 }; - utils::bitset8 mDirtyBuffers { 0xFF }; - utils::bitset8 mDirtyOffsets { 0xFF }; + utils::bitset8 mDirtyBuffers; + utils::bitset8 mDirtyOffsets; }; } // namespace backend diff --git a/libs/utils/include/utils/bitset.h b/libs/utils/include/utils/bitset.h index 4d79f61cda8..8844fdb80ac 100644 --- a/libs/utils/include/utils/bitset.h +++ b/libs/utils/include/utils/bitset.h @@ -128,10 +128,6 @@ class UTILS_PUBLIC bitset { std::fill(std::begin(storage), std::end(storage), 0); } - void allOn() noexcept { - std::fill(std::begin(storage), std::end(storage), ~T(0)); - } - void clear() noexcept { reset(); } From 315c8d75bcf22f7778cbccc63dffe4b39378bfab Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 28 Jun 2024 12:12:27 -0700 Subject: [PATCH 15/49] vk: repair transmission + MSAA That code was changed as part as what looked like a refactoring unrelated to the descriptor-set change itself. Reverting that thunk fixes the problem, however the revert doesn't have the layout transitions that were added. This PR reverts back to the old behavior, but adds the layout transitions. It looks like the refactored code didn't handle "auto resolve" anymore. --- filament/backend/src/vulkan/VulkanDriver.cpp | 28 +++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 83413208a15..a9e402e3ec5 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1328,28 +1328,36 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP fbkey.resolve[i] = VK_NULL_HANDLE; continue; } - auto const& range = attachment.getSubresourceRange(); - attachment.texture->transitionLayout(&commands, range, VulkanLayout::COLOR_ATTACHMENT); - renderPassAttachments.insert(attachment); if (fbkey.samples == 1) { + auto const& range = attachment.getSubresourceRange(); + attachment.texture->transitionLayout(&commands, + range, VulkanLayout::COLOR_ATTACHMENT); + renderPassAttachments.insert(attachment); fbkey.color[i] = attachment.getImageView(); fbkey.resolve[i] = VK_NULL_HANDLE; assert_invariant(fbkey.color[i]); } else { auto& msaaColorAttachment = rt->getMsaaColor(i); auto const& msaaRange = attachment.getSubresourceRange(); + msaaColorAttachment.texture->transitionLayout(&commands, + msaaRange, VulkanLayout::COLOR_ATTACHMENT); renderPassAttachments.insert(msaaColorAttachment); - msaaColorAttachment.texture->transitionLayout(&commands, msaaRange, - VulkanLayout::COLOR_ATTACHMENT); + fbkey.color[i] = msaaColorAttachment.getImageView(); - assert_invariant(fbkey.color[i]); - assert_invariant(attachment.texture->samples == 1); + VulkanTexture* texture = attachment.texture; + if (texture->samples == 1) { + mRenderPassFboInfo.hasColorResolve = true; - mRenderPassFboInfo.hasColorResolve = true; - fbkey.resolve[i] = attachment.getImageView(); - assert_invariant(fbkey.resolve[i]); + auto const& range = attachment.getSubresourceRange(); + attachment.texture->transitionLayout(&commands, + range, VulkanLayout::COLOR_ATTACHMENT); + renderPassAttachments.insert(attachment); + fbkey.resolve[i] = attachment.getImageView(); + assert_invariant(fbkey.resolve[i]); + } + assert_invariant(fbkey.color[i]); } } if (depth.texture) { From d2cb53e39dc0955da688e45cc5b48477fb9ecd13 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 28 Jun 2024 10:29:45 -0700 Subject: [PATCH 16/49] add minimal support for Texture Views in the backends Texture Views are currently limited to the min/max lod, and it's not allowed to have several views of the same texture in a single shader. Only SAMPLEABLE textures can have a texture view. Additionally, setMinMaxLevels() is removed as well as all automatic min/max lods adjustments. This is all handled by filament itself from now on, using Texture Views. --- .../include/private/backend/DriverAPI.inc | 10 +- filament/backend/src/metal/MetalDriver.mm | 24 +- filament/backend/src/metal/MetalHandles.h | 28 +-- filament/backend/src/metal/MetalHandles.mm | 56 ++--- filament/backend/src/noop/NoopDriver.cpp | 3 - .../backend/src/opengl/GLDescriptorSet.cpp | 57 ++++- filament/backend/src/opengl/GLDescriptorSet.h | 21 +- filament/backend/src/opengl/GLTexture.h | 10 +- filament/backend/src/opengl/OpenGLDriver.cpp | 188 +++++++-------- filament/backend/src/opengl/OpenGLDriver.h | 2 - filament/backend/src/opengl/OpenGLProgram.cpp | 4 +- filament/backend/src/vulkan/VulkanDriver.cpp | 41 ++-- filament/backend/src/vulkan/VulkanHandles.cpp | 5 +- filament/backend/src/vulkan/VulkanHandles.h | 4 +- .../backend/src/vulkan/VulkanSwapChain.cpp | 12 +- filament/backend/src/vulkan/VulkanSwapChain.h | 5 +- filament/backend/src/vulkan/VulkanTexture.cpp | 216 +++++++++++------- filament/backend/src/vulkan/VulkanTexture.h | 138 +++++++---- filament/backend/test/test_FeedbackLoops.cpp | 37 +-- filament/backend/test/test_MipLevels.cpp | 25 +- filament/docs/Vulkan.md.html | 2 +- filament/src/PostProcessManager.cpp | 67 +++--- filament/src/PostProcessManager.h | 2 +- filament/src/ShadowMapManager.cpp | 4 +- filament/src/details/IndirectLight.cpp | 6 +- filament/src/details/MaterialInstance.cpp | 29 ++- filament/src/details/MaterialInstance.h | 10 + filament/src/details/RenderTarget.cpp | 4 +- filament/src/details/Texture.cpp | 64 ++++++ filament/src/details/Texture.h | 27 ++- filament/src/ds/ColorPassDescriptorSet.cpp | 5 +- 31 files changed, 669 insertions(+), 437 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 9575b1b8e7c..924a1f92ad0 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -196,6 +196,11 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTexture, uint32_t, depth, backend::TextureUsage, usage) +DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureView, + backend::TextureHandle, texture, + uint8_t, baseLevel, + uint8_t, levelCount) + DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureSwizzled, backend::SamplerType, target, uint8_t, levels, @@ -374,11 +379,6 @@ DECL_DRIVER_API_N(updateSamplerGroup, backend::SamplerGroupHandle, ubh, backend::BufferDescriptor&&, data) -DECL_DRIVER_API_N(setMinMaxLevels, - backend::TextureHandle, th, - uint32_t, minLevel, - uint32_t, maxLevel) - DECL_DRIVER_API_N(update3DImage, backend::TextureHandle, th, uint32_t, level, diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index e97aebbcda4..871ef482d96 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -438,6 +438,13 @@ th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); } +void MetalDriver::createTextureViewR(Handle th, + Handle srch, uint8_t baseLevel, uint8_t levelCount) { + MetalTexture const* src = handle_cast(srch); + mContext->textures.insert(construct_handle(th, *mContext, + src, baseLevel, levelCount)); +} + void MetalDriver::importTextureR(Handle th, intptr_t i, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, @@ -501,7 +508,6 @@ auto colorTexture = handle_cast(buffer.handle); FILAMENT_CHECK_PRECONDITION(colorTexture->getMtlTextureForWrite()) << "Color texture passed to render target has no texture allocation"; - colorTexture->extendLodRangeTo(buffer.level); colorAttachments[i] = { colorTexture, color[i].level, color[i].layer }; } @@ -512,7 +518,6 @@ auto depthTexture = handle_cast(depth.handle); FILAMENT_CHECK_PRECONDITION(depthTexture->getMtlTextureForWrite()) << "Depth texture passed to render target has no texture allocation."; - depthTexture->extendLodRangeTo(depth.level); depthAttachment = { depthTexture, depth.level, depth.layer }; } @@ -523,7 +528,6 @@ auto stencilTexture = handle_cast(stencil.handle); FILAMENT_CHECK_PRECONDITION(stencilTexture->getMtlTextureForWrite()) << "Stencil texture passed to render target has no texture allocation."; - stencilTexture->extendLodRangeTo(stencil.level); stencilAttachment = { stencilTexture, stencil.level, stencil.layer }; } @@ -643,6 +647,10 @@ return alloc_handle(); } +Handle MetalDriver::createTextureViewS() noexcept { + return alloc_handle(); +} + Handle MetalDriver::importTextureS() noexcept { return alloc_handle(); } @@ -1084,14 +1092,6 @@ vertexBuffer->buffers[index] = bufferObject->getBuffer(); } -void MetalDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { - auto tex = handle_cast(th); - tex->setLodRange(minLevel, maxLevel); - - DEBUG_LOG("setMinMaxLevels(th = %d, minLevel = %d, maxLevel = %d)\n", th.getId(), minLevel, - maxLevel); -} - void MetalDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, @@ -1663,8 +1663,6 @@ mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blit/resolve"); - dstTexture->extendLodRangeTo(dstLevel); - DEBUG_LOG( "blit(dst = %d, srcLevel = %d, srcLayer = %d, dstOrigin = (%d, %d), src = %d, dstLevel " "= %d, dstLayer = %d, srcOrigin = (%d, %d), size = (%d, %d))\n", diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index f723a37aeb5..25de9c935dc 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -229,8 +229,11 @@ class MetalTexture : public HwTexture { public: MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, - TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) - noexcept; + TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) noexcept; + + // constructor for creating a texture view + MetalTexture(MetalContext& context, + MetalTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept; // Constructor for importing an id outside of Filament. MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, @@ -239,28 +242,17 @@ class MetalTexture : public HwTexture { ~MetalTexture(); - // Returns an id suitable for reading in a shader, taking into account swizzle and - // LOD clamping. - id getMtlTextureForRead() noexcept; + // Returns an id suitable for reading in a shader, taking into account swizzle. + id getMtlTextureForRead() const noexcept; // Returns the id for attaching to a render pass. - id getMtlTextureForWrite() noexcept { + id getMtlTextureForWrite() const noexcept { return texture; } void loadImage(uint32_t level, MTLRegion region, PixelBufferDescriptor& p) noexcept; void generateMipmaps() noexcept; - // A texture starts out with none of its mip levels (also referred to as LODs) available for - // reading. 4 actions update the range of LODs available: - // - calling loadImage - // - calling generateMipmaps - // - using the texture as a render target attachment - // - calling setMinMaxLevels - // A texture's available mips are consistent throughout a render pass. - void setLodRange(uint16_t minLevel, uint16_t maxLevel); - void extendLodRangeTo(uint16_t level); - static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format); MetalContext& context; @@ -307,10 +299,6 @@ class MetalTexture : public HwTexture { // Filament swizzling only affects texture reads, so this should not be used when the texture is // bound as a render target attachment. id swizzledTextureView = nil; - id lodTextureView = nil; - - uint16_t minLod = std::numeric_limits::max(); - uint16_t maxLod = 0; bool terminated = false; }; diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index fc71c8a8958..a99f64317da 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -615,19 +615,27 @@ static void func(void* user) { } } +MetalTexture::MetalTexture(MetalContext& context, + MetalTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept + : HwTexture(src->target, src->levels, src->samples, + src->width, src->height, src->depth, src->format, src->usage), + context(context), + externalImage(context) { + texture = createTextureViewWithLodRange( + src->getMtlTextureForRead(), baseLevel, baseLevel + levelCount - 1); +} + MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, id metalTexture) noexcept : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context), externalImage(context) { texture = metalTexture; - setLodRange(0, levels - 1); } void MetalTexture::terminate() noexcept { texture = nil; swizzledTextureView = nil; - lodTextureView = nil; msaaSidecar = nil; externalImage.set(nullptr); terminated = true; @@ -637,29 +645,9 @@ static void func(void* user) { externalImage.set(nullptr); } -id MetalTexture::getMtlTextureForRead() noexcept { - // TODO: get LODs to work correctly - id tex = swizzledTextureView ? swizzledTextureView : texture; - return tex; - if (lodTextureView) { - return lodTextureView; - } - // The texture's swizzle remains constant throughout its lifetime, however its LOD range can - // change. We'll cache the LOD view, and set lodTextureView to nil if minLod or maxLod is - // updated. - // TODO: ah, but we can't set it to nil because we need to keep it alive if it has been latched. - id t = swizzledTextureView ? swizzledTextureView : texture; - if (!t) { - return nil; - } - if (UTILS_UNLIKELY(minLod > maxLod)) { - // If the texture does not have any available LODs, provide a view of only level 0. - // Filament should prevent this from ever occurring. - lodTextureView = createTextureViewWithLodRange(t, 0, 0); - return lodTextureView; - } - lodTextureView = createTextureViewWithLodRange(t, minLod, maxLod); - return lodTextureView; +id MetalTexture::getMtlTextureForRead() const noexcept { + // TODO: get LODs to work correctly with swizzling + return swizzledTextureView ? swizzledTextureView : texture; } MTLPixelFormat MetalTexture::decidePixelFormat(MetalContext* context, TextureFormat format) { @@ -778,15 +766,12 @@ static void func(void* user) { assert_invariant(false); } } - - extendLodRangeTo(level); } void MetalTexture::generateMipmaps() noexcept { id blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder]; [blitEncoder generateMipmapsForTexture:texture]; [blitEncoder endEncoding]; - setLodRange(0, texture.mipmapLevelCount - 1); } void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice, @@ -910,21 +895,6 @@ static void func(void* user) { context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit"); } -void MetalTexture::extendLodRangeTo(uint16_t level) { - assert_invariant(!isInRenderPass(&context)); - minLod = std::min(minLod, level); - maxLod = std::max(maxLod, level); - lodTextureView = nil; -} - -void MetalTexture::setLodRange(uint16_t min, uint16_t max) { - assert_invariant(!isInRenderPass(&context)); - assert_invariant(min <= max); - minLod = min; - maxLod = max; - lodTextureView = nil; -} - void MetalSamplerGroup::finalize() { assert_invariant(encoder); // TODO: we should be able to encode textures and samplers inside setFinalizedTexture and diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index ba7d6b92d5b..e81244601a3 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -250,9 +250,6 @@ void NoopDriver::setVertexBufferObject(Handle vbh, uint32_t inde Handle boh) { } -void NoopDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { -} - void NoopDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, diff --git a/filament/backend/src/opengl/GLDescriptorSet.cpp b/filament/backend/src/opengl/GLDescriptorSet.cpp index b8070b64b15..0667f8497ed 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.cpp +++ b/filament/backend/src/opengl/GLDescriptorSet.cpp @@ -20,6 +20,7 @@ #include "GLDescriptorSetLayout.h" #include "GLTexture.h" #include "GLUtils.h" +#include "OpenGLDriver.h" #include "OpenGLContext.h" #include "OpenGLProgram.h" @@ -168,9 +169,16 @@ void GLDescriptorSet::update(OpenGLContext& gl, arg.target = t ? t->gl.target : 0; arg.id = t ? t->gl.id : 0; - arg.anisotropy = float(1u << params.anisotropyLog2); if constexpr (std::is_same_v || std::is_same_v) { + if constexpr (std::is_same_v) { + arg.anisotropy = float(1u << params.anisotropyLog2); + } + if (t) { + arg.ref = t->ref; + arg.baseLevel = t->gl.baseLevel; + arg.maxLevel = t->gl.maxLevel; + } #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 arg.sampler = gl.getSampler(params); #else @@ -186,8 +194,36 @@ void GLDescriptorSet::update(OpenGLContext& gl, }, descriptors[binding].desc); } -void GLDescriptorSet::bind(OpenGLContext& gl, OpenGLProgram const& p, descriptor_set_t set, - uint32_t const* offsets, bool offsetsOnly) const noexcept { +template +void GLDescriptorSet::updateTextureLod(OpenGLContext& gl, + HandleAllocatorGL& handleAllocator, GLuint unit, T const& desc) noexcept { + // The common case is that we don't have a ref handle (we only have one if + // the texture ever had a View on it. + GLTextureRef* const ref = handleAllocator.handle_cast(desc.ref); + if (UTILS_UNLIKELY((desc.baseLevel != ref->baseLevel + || desc.maxLevel != ref->maxLevel))) { + // If we have views, then it's still uncommon that we'll switch often + // handle the case where we reset to the original texture + GLint baseLevel = desc.baseLevel; + GLint maxLevel = desc.maxLevel; + if (baseLevel > maxLevel) { + baseLevel = 0; + maxLevel = 1000; // per OpenGL spec + } + // that is very unfortunate that we have to call activeTexture here + gl.activeTexture(unit); + glTexParameteri(desc.target, GL_TEXTURE_BASE_LEVEL, baseLevel); + glTexParameteri(desc.target, GL_TEXTURE_MAX_LEVEL, maxLevel); + ref->baseLevel = desc.baseLevel; + ref->maxLevel = desc.maxLevel; + } +} + +void GLDescriptorSet::bind( + OpenGLContext& gl, + HandleAllocatorGL& handleAllocator, + OpenGLProgram const& p, + descriptor_set_t set, uint32_t const* offsets, bool offsetsOnly) const noexcept { // TODO: check that offsets is sized correctly size_t dynamicOffsetIndex = 0; @@ -197,7 +233,8 @@ void GLDescriptorSet::bind(OpenGLContext& gl, OpenGLProgram const& p, descriptor } // loop only over the active indices for this program - activeDescriptorBindings.forEachSetBit([this, &gl, &p, set, offsets, &dynamicOffsetIndex] + activeDescriptorBindings.forEachSetBit( + [this,&gl, &handleAllocator, &p, set, offsets, &dynamicOffsetIndex] (size_t binding) { // This would fail here if we're trying to set a descriptor that doesn't exist in the @@ -205,7 +242,9 @@ void GLDescriptorSet::bind(OpenGLContext& gl, OpenGLProgram const& p, descriptor assert_invariant(binding < descriptors.size()); auto const& entry = descriptors[binding]; - std::visit([&gl, &p, &dynamicOffsetIndex, set, binding, offsets](auto&& arg) { + std::visit( + [&gl, &handleAllocator, &p, &dynamicOffsetIndex, set, binding, offsets] + (auto&& arg) { using T = std::decay_t; if constexpr (std::is_same_v) { GLuint const bindingPoint = p.getBufferBinding(set, binding); @@ -232,6 +271,9 @@ void GLDescriptorSet::bind(OpenGLContext& gl, OpenGLProgram const& p, descriptor if (arg.target) { gl.bindTexture(unit, arg.target, arg.id); gl.bindSampler(unit, arg.sampler); + if (UTILS_UNLIKELY(arg.ref)) { + updateTextureLod(gl, handleAllocator, unit, arg); + } } else { gl.unbindTextureUnit(unit); } @@ -240,11 +282,14 @@ void GLDescriptorSet::bind(OpenGLContext& gl, OpenGLProgram const& p, descriptor if (arg.target) { gl.bindTexture(unit, arg.target, arg.id); gl.bindSampler(unit, arg.sampler); + if (UTILS_UNLIKELY(arg.ref)) { + updateTextureLod(gl, handleAllocator, unit, arg); + } #if defined(GL_EXT_texture_filter_anisotropic) // Driver claims to support anisotropic filtering, but it fails when set on // the sampler, we have to set it on the texture instead. glTexParameterf(arg.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, - std::min(gl.gets.max_anisotropy, arg.anisotropy)); + std::min(gl.gets.max_anisotropy, float(arg.anisotropy))); #endif } else { gl.unbindTextureUnit(unit); diff --git a/filament/backend/src/opengl/GLDescriptorSet.h b/filament/backend/src/opengl/GLDescriptorSet.h index 72d42fec33c..db2548c916b 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.h +++ b/filament/backend/src/opengl/GLDescriptorSet.h @@ -29,6 +29,8 @@ #include #include +#include + #include #include @@ -38,6 +40,7 @@ namespace filament::backend { struct GLBufferObject; struct GLTexture; +struct GLTextureRef; struct GLDescriptorSetLayout; class OpenGLProgram; class OpenGLContext; @@ -59,7 +62,10 @@ struct GLDescriptorSet : public HwDescriptorSet { descriptor_binding_t binding, GLTexture* t, SamplerParams params) noexcept; // conceptually bind the set to the command buffer - void bind(OpenGLContext& gl, OpenGLProgram const& p, + void bind( + OpenGLContext& gl, + HandleAllocatorGL& handleAllocator, + OpenGLProgram const& p, descriptor_set_t set, uint32_t const* offsets, bool offsetsOnly) const noexcept; uint32_t getDynamicBufferCount() const noexcept { @@ -103,14 +109,19 @@ struct GLDescriptorSet : public HwDescriptorSet { GLenum target = 0; // 4 GLuint id = 0; // 4 GLuint sampler = 0; // 4 - float anisotropy = 1.0f; // 4 + Handle ref; // 4 + int8_t baseLevel = 0x7f; // 1 + int8_t maxLevel = -1; // 1 }; struct SamplerWithAnisotropyWorkaround { GLenum target = 0; // 4 GLuint id = 0; // 4 GLuint sampler = 0; // 4 - float anisotropy = 1.0f; // 4 + Handle ref; // 4 + math::half anisotropy = 1.0f; // 2 + int8_t baseLevel = 0x7f; // 1 + int8_t maxLevel = -1; // 1 }; // A sampler descriptor for ES2 @@ -131,6 +142,10 @@ struct GLDescriptorSet : public HwDescriptorSet { }; static_assert(sizeof(Descriptor) <= 32); + template + static void updateTextureLod(OpenGLContext& gl, + HandleAllocatorGL& handleAllocator, GLuint unit, T const& desc) noexcept; + utils::FixedCapacityVector descriptors; // 16 utils::bitset64 dynamicBuffers; // 8 DescriptorSetLayoutHandle dslh; // 4 diff --git a/filament/backend/src/opengl/GLTexture.h b/filament/backend/src/opengl/GLTexture.h index 5e9460e17a5..63a18a4d270 100644 --- a/filament/backend/src/opengl/GLTexture.h +++ b/filament/backend/src/opengl/GLTexture.h @@ -21,12 +21,20 @@ #include "gl_headers.h" +#include #include #include namespace filament::backend { +struct GLTextureRef { + GLTextureRef() = default; + uint16_t count = 1; + int8_t baseLevel = 127; + int8_t maxLevel = -1; +}; + struct GLTexture : public HwTexture { using HwTexture::HwTexture; struct GL { @@ -45,7 +53,7 @@ struct GLTexture : public HwTexture { uint8_t sidecarSamples : 4; uint8_t reserved1 : 3; } gl; - + mutable Handle ref; OpenGLPlatform::ExternalTexture* externalTexture = nullptr; }; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 15e4692849d..0db682a0b3d 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -17,6 +17,7 @@ #include "OpenGLDriver.h" #include "CommandStreamDispatcher.h" +#include "GLTexture.h" #include "GLUtils.h" #include "OpenGLContext.h" #include "OpenGLDriverFactory.h" @@ -40,6 +41,7 @@ #include "private/backend/Dispatcher.h" #include "private/backend/DriverApi.h" +#include #include #include #include @@ -512,6 +514,10 @@ Handle OpenGLDriver::createTextureSwizzledS() noexcept { return initHandle(); } +Handle OpenGLDriver::createTextureViewS() noexcept { + return initHandle(); +} + Handle OpenGLDriver::importTextureS() noexcept { return initHandle(); } @@ -901,6 +907,52 @@ void OpenGLDriver::createTextureSwizzledR(Handle th, CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::createTextureViewR(Handle th, + Handle srch, uint8_t baseLevel, uint8_t levelCount) { + DEBUG_MARKER() + GLTexture const* const src = handle_cast(srch); + + FILAMENT_CHECK_PRECONDITION(any(src->usage & TextureUsage::SAMPLEABLE)) + << "TextureView can only be created on a SAMPLEABLE texture"; + + FILAMENT_CHECK_PRECONDITION(!src->gl.imported) + << "TextureView can't be created on imported textures"; + + if (!src->ref) { + // lazily create the ref handle, because most textures will never get a texture view + src->ref = initHandle(); + } + + GLTexture* t = construct(th, + src->target, + src->levels, + src->samples, + src->width, src->height, src->depth, + src->format, + src->usage); + + t->gl = src->gl; + t->gl.sidecarRenderBufferMS = 0; + t->gl.sidecarSamples = 1; + + auto srcBaseLevel = src->gl.baseLevel; + auto srcMaxLevel = src->gl.maxLevel; + if (srcBaseLevel > srcMaxLevel) { + srcBaseLevel = 0; + srcMaxLevel = 127; + } + t->gl.baseLevel = (int8_t)std::min(127, srcBaseLevel + baseLevel); + t->gl.maxLevel = (int8_t)std::min(127, srcBaseLevel + baseLevel + levelCount - 1); + + // increase reference count to this texture handle + t->ref = src->ref; + GLTextureRef* ref = handle_cast(t->ref); + assert_invariant(ref); + ref->count++; + + CHECK_GL_ERROR(utils::slog.e) +} + void OpenGLDriver::importTextureR(Handle th, intptr_t id, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) { @@ -1305,14 +1357,6 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo, rt->gl.resolve |= resolveFlags; - if (any(t->usage & TextureUsage::SAMPLEABLE)) { - // In a sense, drawing to a texture level is similar to calling setTextureData on it; in - // both cases, we update the base/max LOD to give shaders access to levels as they become - // available. Note that this can only expand the LOD range (never shrink it), and that - // users can override this range by calling setMinMaxLevels(). - updateTextureLodRange(t, (int8_t)binfo.level); - } - CHECK_GL_ERROR(utils::slog.e) CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER) } @@ -1653,16 +1697,35 @@ void OpenGLDriver::destroyTexture(Handle th) { if (th) { auto& gl = mContext; GLTexture* t = handle_cast(th); + if (UTILS_LIKELY(!t->gl.imported)) { if (UTILS_LIKELY(t->usage & TextureUsage::SAMPLEABLE)) { - gl.unbindTexture(t->gl.target, t->gl.id); - if (UTILS_UNLIKELY(t->hwStream)) { - detachStream(t); + // drop a reference + uint16_t count = 0; + if (UTILS_UNLIKELY(t->ref)) { + // the common case is that we don't have a ref handle + GLTextureRef* const ref = handle_cast(t->ref); + count = --(ref->count); + if (count == 0) { + destruct(t->ref, ref); + } } - if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) { - mPlatform.destroyExternalImage(t->externalTexture); + if (count == 0) { + // if this was the last reference, we destroy the refcount as well as + // the GL texture name itself. + gl.unbindTexture(t->gl.target, t->gl.id); + if (UTILS_UNLIKELY(t->hwStream)) { + detachStream(t); + } + if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) { + mPlatform.destroyExternalImage(t->externalTexture); + } else { + glDeleteTextures(1, &t->gl.id); + } } else { - glDeleteTextures(1, &t->gl.id); + // The Handle is always destroyed. For extra precaution we also + // check that the GLTexture has a trivial destructor. + static_assert(std::is_trivially_destructible_v); } } else { assert_invariant(t->gl.target == GL_RENDERBUFFER); @@ -2361,28 +2424,6 @@ void OpenGLDriver::updateSamplerGroup(Handle sbh, BufferDescript // TODO: goes away } -void OpenGLDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { - DEBUG_MARKER() - -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - auto& gl = mContext; - if (!gl.isES2()) { - GLTexture* t = handle_cast(th); - bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t); - gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING); - - // Must fit within int8_t. - assert_invariant(minLevel <= 0x7f && maxLevel <= 0x7f); - - t->gl.baseLevel = (int8_t)minLevel; - glTexParameteri(t->gl.target, GL_TEXTURE_BASE_LEVEL, t->gl.baseLevel); - - t->gl.maxLevel = (int8_t)maxLevel; // NOTE: according to the GL spec, the default value of this 1000 - glTexParameteri(t->gl.target, GL_TEXTURE_MAX_LEVEL, t->gl.maxLevel); - } -#endif -} - void OpenGLDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, @@ -2412,16 +2453,6 @@ void OpenGLDriver::generateMipmaps(Handle th) { bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t); gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING); - t->gl.baseLevel = 0; - t->gl.maxLevel = static_cast(t->levels - 1); - -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - if (!gl.isES2()) { - glTexParameteri(t->gl.target, GL_TEXTURE_BASE_LEVEL, t->gl.baseLevel); - glTexParameteri(t->gl.target, GL_TEXTURE_MAX_LEVEL, t->gl.maxLevel); - } -#endif - glGenerateMipmap(t->gl.target); CHECK_GL_ERROR(utils::slog.e) @@ -2531,21 +2562,6 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level, } } -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - if (!gl.isES2()) { - // Update the base/max LOD, so we don't access undefined LOD. this allows the app to - // specify levels as they become available. - if (int8_t(level) < t->gl.baseLevel) { - t->gl.baseLevel = int8_t(level); - glTexParameteri(t->gl.target, GL_TEXTURE_BASE_LEVEL, t->gl.baseLevel); - } - if (int8_t(level) > t->gl.maxLevel) { - t->gl.maxLevel = int8_t(level); - glTexParameteri(t->gl.target, GL_TEXTURE_MAX_LEVEL, t->gl.maxLevel); - } - } -#endif - scheduleDestroy(std::move(p)); CHECK_GL_ERROR(utils::slog.e) @@ -2632,21 +2648,6 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level, } } -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - if (!gl.isES2()) { - // Update the base/max LOD, so we don't access undefined LOD. this allows the app to - // specify levels as they become available. - if (int8_t(level) < t->gl.baseLevel) { - t->gl.baseLevel = int8_t(level); - glTexParameteri(t->gl.target, GL_TEXTURE_BASE_LEVEL, t->gl.baseLevel); - } - if (int8_t(level) > t->gl.maxLevel) { - t->gl.maxLevel = int8_t(level); - glTexParameteri(t->gl.target, GL_TEXTURE_MAX_LEVEL, t->gl.maxLevel); - } - } -#endif - scheduleDestroy(std::move(p)); CHECK_GL_ERROR(utils::slog.e) @@ -3683,15 +3684,6 @@ void OpenGLDriver::blit( gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER); gl.unbindFramebuffer(GL_READ_FRAMEBUFFER); glDeleteFramebuffers(2, fbo); - - if (any(d->usage & TextureUsage::SAMPLEABLE)) { - // In a sense, blitting to a texture level is similar to calling setTextureData on it; in - // both cases, we update the base/max LOD to give shaders access to levels as they become - // available. Note that this can only expand the LOD range (never shrink it), and that - // users can override this range by calling setMinMaxLevels(). - updateTextureLodRange(d, int8_t(dstLevel)); - } - #endif } @@ -3762,29 +3754,6 @@ void OpenGLDriver::blitDEPRECATED(TargetBufferFlags buffers, #endif } -void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel) noexcept { -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - auto& gl = mContext; - if (!gl.isES2()) { - if (texture && any(texture->usage & TextureUsage::SAMPLEABLE)) { - if (targetLevel < texture->gl.baseLevel || targetLevel > texture->gl.maxLevel) { - bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, texture); - gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING); - if (targetLevel < texture->gl.baseLevel) { - texture->gl.baseLevel = targetLevel; - glTexParameteri(texture->gl.target, GL_TEXTURE_BASE_LEVEL, targetLevel); - } - if (targetLevel > texture->gl.maxLevel) { - texture->gl.maxLevel = targetLevel; - glTexParameteri(texture->gl.target, GL_TEXTURE_MAX_LEVEL, targetLevel); - } - } - CHECK_GL_ERROR(utils::slog.e) - } - } -#endif -} - void OpenGLDriver::bindPipeline(PipelineState const& state) { DEBUG_MARKER() auto& gl = mContext; @@ -3863,7 +3832,8 @@ void OpenGLDriver::updateDescriptors(utils::bitset8 invalidDescriptorSets) noexc ds->validate(mHandleAllocator, mCurrentSetLayout[set]); } #endif - ds->bind(context, boundProgram, set, entry.offsets.data(), offsetOnly[set]); + ds->bind(context, mHandleAllocator, boundProgram, + set, entry.offsets.data(), offsetOnly[set]); } }); mInvalidDescriptorSetBindings.clear(); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index fda2b3bf20a..0112d215b3c 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -358,8 +358,6 @@ class OpenGLDriver final : public DriverBase { void detachStream(GLTexture* t) noexcept; void replaceStream(GLTexture* t, GLStream* stream) noexcept; - void updateTextureLodRange(GLTexture* texture, int8_t targetLevel) noexcept; - #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 // tasks executed on the main thread after the fence signaled void whenGpuCommandsComplete(const std::function& fn) noexcept; diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index eadb53fc92b..46f515c0a27 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -17,6 +17,7 @@ #include "OpenGLProgram.h" #include "GLUtils.h" +#include "GLTexture.h" #include "OpenGLDriver.h" #include "ShaderCompilerService.h" @@ -24,8 +25,9 @@ #include #include -#include +#include #include +#include #include #include #include diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index a9e402e3ec5..8bfd1ef22d0 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -90,9 +90,9 @@ VmaAllocator createAllocator(VkInstance instance, VkPhysicalDevice physicalDevic VulkanTexture* createEmptyTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanStagePool& stagePool) { + VulkanResourceAllocator* handleAllocator, VulkanStagePool& stagePool) { VulkanTexture* emptyTexture = new VulkanTexture(device, physicalDevice, context, allocator, - commands, SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1, 1, 1, 1, + commands, handleAllocator, SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT | TextureUsage::COLOR_ATTACHMENT | TextureUsage::SUBPASS_INPUT, stagePool, true /* heap allocated */); uint32_t black = 0; @@ -257,7 +257,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex mTimestamps = std::make_unique(mPlatform->getDevice()); mEmptyTexture = createEmptyTexture(mPlatform->getDevice(), mPlatform->getPhysicalDevice(), - mContext, mAllocator, &mCommands, mStagePool); + mContext, mAllocator, &mCommands, &mResourceAllocator, mStagePool); mEmptyBufferObject = createEmptyBufferObject(mAllocator, mStagePool, &mCommands); mDescriptorSetManager.setPlaceHolders(mSamplerCache.getSampler({}), mEmptyTexture, @@ -553,7 +553,8 @@ void VulkanDriver::createTextureR(Handle th, SamplerType target, uint TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) { auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels, + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, + target, levels, format, samples, w, h, depth, usage, mStagePool); mResourceManager.acquire(vktexture); } @@ -565,8 +566,18 @@ void VulkanDriver::createTextureSwizzledR(Handle th, SamplerType targ TextureSwizzle swizzleArray[] = {r, g, b, a}; const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray); auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, target, levels, - format, samples, w, h, depth, usage, mStagePool, false /*heap allocated */, swizzleMap); + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, + target, levels, format, samples, w, h, depth, usage, mStagePool, + false /*heap allocated */, swizzleMap); + mResourceManager.acquire(vktexture); +} + +void VulkanDriver::createTextureViewR(Handle th, + Handle srch, uint8_t baseLevel, uint8_t levelCount) { + VulkanTexture const* src = mResourceAllocator.handle_cast(srch); + auto vktexture = mResourceAllocator.construct(th, + mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, + &mResourceAllocator, src, baseLevel, levelCount, mStagePool); mResourceManager.acquire(vktexture); } @@ -660,7 +671,8 @@ void VulkanDriver::createRenderTargetR(Handle rth, assert_invariant(tmin.x >= width && tmin.y >= height); auto renderTarget = mResourceAllocator.construct(rth, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height, + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, + width, height, samples, colorTargets, depthStencil, mStagePool); mResourceManager.acquire(renderTarget); } @@ -689,7 +701,7 @@ void VulkanDriver::createSwapChainR(Handle sch, void* nativeWindow, flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE); } auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, - mAllocator, &mCommands, mStagePool, nativeWindow, flags); + mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags); mResourceManager.acquire(swapChain); } @@ -702,7 +714,8 @@ void VulkanDriver::createSwapChainHeadlessR(Handle sch, uint32_t wi } assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions."); auto swapChain = mResourceAllocator.construct(sch, mPlatform, mContext, - mAllocator, &mCommands, mStagePool, nullptr, flags, VkExtent2D{width, height}); + mAllocator, &mCommands, &mResourceAllocator, mStagePool, + nullptr, flags, VkExtent2D{width, height}); mResourceManager.acquire(swapChain); } @@ -750,6 +763,10 @@ Handle VulkanDriver::createTextureSwizzledS() noexcept { return mResourceAllocator.allocHandle(); } +Handle VulkanDriver::createTextureViewS() noexcept { + return mResourceAllocator.allocHandle(); +} + Handle VulkanDriver::importTextureS() noexcept { return mResourceAllocator.allocHandle(); } @@ -1128,11 +1145,6 @@ void VulkanDriver::resetBufferObject(Handle boh) { // This is only useful if updateBufferObjectUnsynchronized() is implemented unsynchronizedly. } -void VulkanDriver::setMinMaxLevels(Handle th, uint32_t minLevel, uint32_t maxLevel) { - auto texture = mResourceAllocator.handle_cast(th); - texture->setPrimaryRange(minLevel, maxLevel); -} - void VulkanDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, PixelBufferDescriptor&& data) { @@ -1218,7 +1230,6 @@ void VulkanDriver::generateMipmaps(Handle th) { srcw = dstw; srch = dsth; } while ((srcw > 1 || srch > 1) && level < t->levels); - t->setPrimaryRange(0, t->levels - 1); } void VulkanDriver::updateSamplerGroup(Handle sbh, diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 7a2041f9fdd..e37b80d27af 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -320,6 +320,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) { VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, uint32_t width, uint32_t height, uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], VulkanAttachment depthStencil[2], VulkanStagePool& stagePool) @@ -352,6 +353,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica if (UTILS_UNLIKELY(!msTexture)) { // TODO: This should be allocated with the ResourceAllocator. msTexture = new VulkanTexture(device, physicalDevice, context, allocator, commands, + handleAllocator, texture->target, ((VulkanTexture const*) texture)->levels, texture->format, samples, texture->width, texture->height, texture->depth, texture->usage, stagePool, true /* heap allocated */); @@ -381,7 +383,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica VulkanTexture* msTexture = depthTexture->getSidecar(); if (UTILS_UNLIKELY(!msTexture)) { msTexture = new VulkanTexture(device, physicalDevice, context, allocator, - commands, depthTexture->target, msLevel, depthTexture->format, samples, + commands, handleAllocator, + depthTexture->target, msLevel, depthTexture->format, samples, depthTexture->width, depthTexture->height, depthTexture->depth, depthTexture->usage, stagePool, true /* heap allocated */); depthTexture->setSidecar(msTexture); diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index bb1f351fd1e..65550d7648b 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -297,7 +297,9 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { // Creates an offscreen render target. VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, - VulkanCommands* commands, uint32_t width, uint32_t height, + VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + uint32_t width, uint32_t height, uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], VulkanAttachment depthStencil[2], VulkanStagePool& stagePool); diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index 0cf75d0f13e..4989b925a38 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -26,12 +26,14 @@ using namespace utils; namespace filament::backend { VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, - VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, + VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, + VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags, VkExtent2D extent) : VulkanResource(VulkanResourceType::SWAP_CHAIN), mPlatform(platform), mCommands(commands), mAllocator(allocator), + mHandleAllocator(handleAllocator), mStagePool(stagePool), mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow), mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize), @@ -62,12 +64,12 @@ void VulkanSwapChain::update() { VkDevice const device = mPlatform->getDevice(); for (auto const color: bundle.colors) { - mColors.push_back(std::make_unique(device, mAllocator, mCommands, color, - bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, + mColors.push_back(std::make_unique(device, mAllocator, mCommands, mHandleAllocator, + color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, TextureUsage::COLOR_ATTACHMENT, mStagePool, true /* heap allocated */)); } - mDepth = std::make_unique(device, mAllocator, mCommands, bundle.depth, - bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, + mDepth = std::make_unique(device, mAllocator, mCommands, mHandleAllocator, + bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, TextureUsage::DEPTH_ATTACHMENT, mStagePool, true /* heap allocated */); mExtent = bundle.extent; diff --git a/filament/backend/src/vulkan/VulkanSwapChain.h b/filament/backend/src/vulkan/VulkanSwapChain.h index 80886f8b1b7..b3e49c044e5 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.h +++ b/filament/backend/src/vulkan/VulkanSwapChain.h @@ -36,11 +36,13 @@ namespace filament::backend { struct VulkanHeadlessSwapChain; struct VulkanSurfaceSwapChain; +class VulkanResourceAllocator; // A wrapper around the platform implementation of swapchain. struct VulkanSwapChain : public HwSwapChain, VulkanResource { VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, VmaAllocator allocator, - VulkanCommands* commands, VulkanStagePool& stagePool, + VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, + VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags, VkExtent2D extent = {0, 0}); ~VulkanSwapChain(); @@ -80,6 +82,7 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource { VulkanPlatform* mPlatform; VulkanCommands* mCommands; VmaAllocator mAllocator; + VulkanResourceAllocator* const mHandleAllocator; VulkanStagePool& mStagePool; bool const mHeadless; bool const mFlushAndWaitOnResize; diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 3d021daff0d..d673454843a 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -15,6 +15,7 @@ */ #include "VulkanMemory.h" +#include "VulkanResourceAllocator.h" #include "VulkanTexture.h" #include "VulkanUtility.h" @@ -28,50 +29,78 @@ using namespace bluevk; namespace filament::backend { -VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, + +VulkanTextureState::VulkanTextureState( + VkDevice device, VmaAllocator allocator, VulkanCommands* commands, + VulkanStagePool& stagePool, + VkFormat format, VkImageViewType viewType, VkComponentMapping swizzle) + : VulkanResource(VulkanResourceType::HEAP_ALLOCATED), + mVkFormat(format), + mViewType(viewType), + mSwizzle(swizzle), + mStagePool(stagePool), + mDevice(device), + mAllocator(allocator), + mCommands(commands) { +} + +VulkanTextureState* VulkanTexture::getSharedState() { + VulkanTextureState* state = mAllocator->handle_cast(mState); + return state; +} + +VulkanTextureState const* VulkanTexture::getSharedState() const { + VulkanTextureState const* state = mAllocator->handle_cast(mState); + return state; +} + +VulkanTexture::VulkanTexture( + VkDevice device, VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated) - : HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, - tusage), - VulkanResource( - heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), - mVkFormat(format), - mViewType(imgutil::getViewType(target)), - mSwizzle({}), - mTextureImage(image), - mFullViewRange{ - .aspectMask = getImageAspect(), - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - mPrimaryViewRange(mFullViewRange), - mStagePool(stagePool), - mDevice(device), - mAllocator(allocator), - mCommands(commands) {} + : HwTexture( + SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, tusage), + VulkanResource( + heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), + mAllocator(handleAllocator), + mState(handleAllocator->initHandle( + device, allocator, commands, stagePool, + format, imgutil::getViewType(SamplerType::SAMPLER_2D), VkComponentMapping{})), + + mFullViewRange{ + .aspectMask = getImageAspect(), + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + mPrimaryViewRange(mFullViewRange) +{ + auto* const state = handleAllocator->handle_cast(mState); + state->mTextureImage = image; +} VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated, VkComponentMapping swizzle) : HwTexture(target, levels, samples, w, h, depth, tformat, tusage), VulkanResource( heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), - mVkFormat(backend::getVkFormat(tformat)), - mViewType(imgutil::getViewType(target)), - mSwizzle(swizzle), - mStagePool(stagePool), - mDevice(device), - mAllocator(allocator), - mCommands(commands) { + mAllocator(handleAllocator), + mState(handleAllocator->initHandle( + device, allocator, commands, stagePool, + backend::getVkFormat(tformat), imgutil::getViewType(target), swizzle)) +{ + auto* const state = handleAllocator->handle_cast(mState); // Create an appropriately-sized device-only VkImage, but do not fill it yet. VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, - .format = mVkFormat, + .format = state->mVkFormat, .extent = {w, h, depth}, .mipLevels = levels, .arrayLayers = 1, @@ -147,7 +176,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, // any kind of attachment (color or depth). const auto& limits = context.getPhysicalDeviceLimits(); if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) { - samples = reduceSampleCount(samples, isVkDepthFormat(mVkFormat) + samples = reduceSampleCount(samples, isVkDepthFormat(state->mVkFormat) ? limits.sampledImageDepthSampleCounts : limits.sampledImageColorSampleCounts); } @@ -161,12 +190,12 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, this->samples = samples; imageInfo.samples = (VkSampleCountFlagBits) samples; - VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage); + VkResult error = vkCreateImage(state->mDevice, &imageInfo, VKALLOC, &state->mTextureImage); if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) { FVK_LOGD << "vkCreateImage: " - << "image = " << mTextureImage << ", " + << "image = " << state->mTextureImage << ", " << "result = " << error << ", " - << "handle = " << utils::io::hex << mTextureImage << utils::io::dec << ", " + << "handle = " << utils::io::hex << state->mTextureImage << utils::io::dec << ", " << "extent = " << w << "x" << h << "x"<< depth << ", " << "mipLevels = " << int(levels) << ", " << "TextureUsage = " << static_cast(usage) << ", " @@ -175,13 +204,13 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, << "type = " << imageInfo.imageType << ", " << "flags = " << imageInfo.flags << ", " << "target = " << static_cast(target) <<", " - << "format = " << mVkFormat << utils::io::endl; + << "format = " << state->mVkFormat << utils::io::endl; } FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image."; // Allocate memory for the VkImage and bind it. VkMemoryRequirements memReqs = {}; - vkGetImageMemoryRequirements(mDevice, mTextureImage, &memReqs); + vkGetImageMemoryRequirements(state->mDevice, state->mTextureImage, &memReqs); uint32_t memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); @@ -194,9 +223,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, .allocationSize = memReqs.size, .memoryTypeIndex = memoryTypeIndex, }; - error = vkAllocateMemory(mDevice, &allocInfo, nullptr, &mTextureImageMemory); + error = vkAllocateMemory(state->mDevice, &allocInfo, nullptr, &state->mTextureImageMemory); FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory."; - error = vkBindImageMemory(mDevice, mTextureImage, mTextureImageMemory, 0); + error = vkBindImageMemory(state->mDevice, state->mTextureImage, state->mTextureImageMemory, 0); FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image."; uint32_t layerCount = 0; @@ -224,20 +253,44 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, mPrimaryViewRange = mFullViewRange; // Go ahead and create the primary image view. - getImageView(mPrimaryViewRange, mViewType, mSwizzle); + getImageView(mPrimaryViewRange, state->mViewType, state->mSwizzle); - VulkanCommandBuffer& commandsBuf = mCommands->get(); + VulkanCommandBuffer& commandsBuf = state->mCommands->get(); commandsBuf.acquire(this); transitionLayout(&commandsBuf, mFullViewRange, imgutil::getDefaultLayout(imageInfo.usage)); } +VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, + VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount, + VulkanStagePool& stagePool) + : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, + src->format, src->usage), + VulkanResource(VulkanResourceType::TEXTURE), + mAllocator(handleAllocator) +{ + mState = src->mState; + auto* state = handleAllocator->handle_cast(mState); + state->refs++; + mFullViewRange = src->mFullViewRange; + mPrimaryViewRange = src->mPrimaryViewRange; + mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel; + mPrimaryViewRange.levelCount = levelCount; +} + VulkanTexture::~VulkanTexture() { - if (mTextureImageMemory != VK_NULL_HANDLE) { - vkDestroyImage(mDevice, mTextureImage, VKALLOC); - vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC); - } - for (auto entry : mCachedImageViews) { - vkDestroyImageView(mDevice, entry.second, VKALLOC); + auto* const state = mAllocator->handle_cast(mState); + state->refs--; + if (state->refs == 0) { + if (state->mTextureImageMemory != VK_NULL_HANDLE) { + vkDestroyImage(state->mDevice, state->mTextureImage, VKALLOC); + vkFreeMemory(state->mDevice, state->mTextureImageMemory, VKALLOC); + } + for (auto entry: state->mCachedImageViews) { + vkDestroyImageView(state->mDevice, entry.second, VKALLOC); + } + mAllocator->destruct(mState); } } @@ -246,6 +299,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt assert_invariant(width <= this->width && height <= this->height); assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP || target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1)); + auto* const state = mAllocator->handle_cast(mState); const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; @@ -258,7 +312,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt // If format conversion is both required and supported, use vkCmdBlitImage. const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type); - const VkFormat deviceFormat = getVkFormatLinear(mVkFormat); + const VkFormat deviceFormat = getVkFormatLinear(state->mVkFormat); if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) { assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 && "Offsets not yet supported when format conversion is required."); @@ -270,14 +324,14 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt // Otherwise, use vkCmdCopyBufferToImage. void* mapped = nullptr; - VulkanStage const* stage = mStagePool.acquireStage(hostData->size); + VulkanStage const* stage = state->mStagePool.acquireStage(hostData->size); assert_invariant(stage->memory); - vmaMapMemory(mAllocator, stage->memory, &mapped); + vmaMapMemory(state->mAllocator, stage->memory, &mapped); memcpy(mapped, hostData->buffer, hostData->size); - vmaUnmapMemory(mAllocator, stage->memory); - vmaFlushAllocation(mAllocator, stage->memory, 0, hostData->size); + vmaUnmapMemory(state->mAllocator, stage->memory); + vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size); - VulkanCommandBuffer& commands = mCommands->get(); + VulkanCommandBuffer& commands = state->mCommands->get(); VkCommandBuffer const cmdbuf = commands.buffer(); commands.acquire(this); @@ -325,22 +379,23 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt transitionLayout(&commands, transitionRange, newLayout); - vkCmdCopyBufferToImage(cmdbuf, stage->buffer, mTextureImage, newVkLayout, 1, ©Region); + vkCmdCopyBufferToImage(cmdbuf, stage->buffer, state->mTextureImage, newVkLayout, 1, ©Region); transitionLayout(&commands, transitionRange, nextLayout); } void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel) { + auto* const state = mAllocator->handle_cast(mState); void* mapped = nullptr; VulkanStageImage const* stage - = mStagePool.acquireImage(hostData.format, hostData.type, width, height); - vmaMapMemory(mAllocator, stage->memory, &mapped); + = state->mStagePool.acquireImage(hostData.format, hostData.type, width, height); + vmaMapMemory(state->mAllocator, stage->memory, &mapped); memcpy(mapped, hostData.buffer, hostData.size); - vmaUnmapMemory(mAllocator, stage->memory); - vmaFlushAllocation(mAllocator, stage->memory, 0, hostData.size); + vmaUnmapMemory(state->mAllocator, stage->memory); + vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData.size); - VulkanCommandBuffer& commands = mCommands->get(); + VulkanCommandBuffer& commands = state->mCommands->get(); VkCommandBuffer const cmdbuf = commands.buffer(); commands.acquire(this); @@ -365,18 +420,11 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u transitionLayout(&commands, range, newLayout); vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC), - mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST); + state->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST); transitionLayout(&commands, range, oldLayout); } -void VulkanTexture::setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel) { - maxMiplevel = filament::math::min(int(maxMiplevel), int(this->levels - 1)); - mPrimaryViewRange.baseMipLevel = minMiplevel; - mPrimaryViewRange.levelCount = maxMiplevel - minMiplevel + 1; - getImageView(mPrimaryViewRange, mViewType, mSwizzle); -} - VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) { // Attachments should only have one mipmap level and one layer. range.levelCount = 1; @@ -385,35 +433,38 @@ VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) { } VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) { - return getImageView(range, type, mSwizzle); + auto* const state = mAllocator->handle_cast(mState); + return getImageView(range, type, state->mSwizzle); } VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) { - ImageViewKey const key {range, viewType, swizzle}; - auto iter = mCachedImageViews.find(key); - if (iter != mCachedImageViews.end()) { + auto* const state = mAllocator->handle_cast(mState); + VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle }; + auto iter = state->mCachedImageViews.find(key); + if (iter != state->mCachedImageViews.end()) { return iter->second; } VkImageViewCreateInfo viewInfo = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, - .image = mTextureImage, + .image = state->mTextureImage, .viewType = viewType, - .format = mVkFormat, + .format = state->mVkFormat, .components = swizzle, .subresourceRange = range, }; VkImageView imageView; - vkCreateImageView(mDevice, &viewInfo, VKALLOC, &imageView); - mCachedImageViews.emplace(key, imageView); + vkCreateImageView(state->mDevice, &viewInfo, VKALLOC, &imageView); + state->mCachedImageViews.emplace(key, imageView); return imageView; } VkImageAspectFlags VulkanTexture::getImageAspect() const { // Helper function in VulkanUtility - return filament::backend::getImageAspect(mVkFormat); + auto* const state = mAllocator->handle_cast(mState); + return filament::backend::getImageAspect(state->mVkFormat); } void VulkanTexture::transitionLayout(VulkanCommandBuffer* commands, @@ -425,6 +476,7 @@ void VulkanTexture::transitionLayout( VkCommandBuffer cmdbuf, std::shared_ptr fence, const VkImageSubresourceRange& range, VulkanLayout newLayout) { + auto* const state = mAllocator->handle_cast(mState); VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); uint32_t const firstLayer = range.baseArrayLayer; @@ -455,7 +507,7 @@ void VulkanTexture::transitionLayout( continue; } hasTransitions = hasTransitions || imgutil::transitionLayout(cmdbuf, { - .image = mTextureImage, + .image = state->mTextureImage, .oldLayout = layout, .newLayout = newLayout, .subresources = { @@ -470,7 +522,7 @@ void VulkanTexture::transitionLayout( } } else if (newLayout != oldLayout) { hasTransitions = imgutil::transitionLayout(cmdbuf, { - .image = mTextureImage, + .image = state->mTextureImage, .oldLayout = oldLayout, .newLayout = newLayout, .subresources = range, @@ -478,7 +530,7 @@ void VulkanTexture::transitionLayout( } if (hasTransitions) { - mTransitionFence = fence; + state->mTransitionFence = fence; setLayout(range, newLayout); #if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) @@ -499,6 +551,7 @@ void VulkanTexture::transitionLayout( } void VulkanTexture::setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout) { + auto* const state = mAllocator->handle_cast(mState); uint32_t const firstLayer = range.baseArrayLayer; uint32_t const lastLayer = firstLayer + range.layerCount; uint32_t const firstLevel = range.baseMipLevel; @@ -511,24 +564,25 @@ void VulkanTexture::setLayout(const VkImageSubresourceRange& range, VulkanLayout for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) { uint32_t const first = (layer << 16) | firstLevel; uint32_t const last = (layer << 16) | lastLevel; - mSubresourceLayouts.clear(first, last); + state->mSubresourceLayouts.clear(first, last); } } else { for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) { uint32_t const first = (layer << 16) | firstLevel; uint32_t const last = (layer << 16) | lastLevel; - mSubresourceLayouts.add(first, last, newLayout); + state->mSubresourceLayouts.add(first, last, newLayout); } } } VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { assert_invariant(level <= 0xffff && layer <= 0xffff); + auto* const state = mAllocator->handle_cast(mState); const uint32_t key = (layer << 16) | level; - if (!mSubresourceLayouts.has(key)) { + if (!state->mSubresourceLayouts.has(key)) { return VulkanLayout::UNDEFINED; } - return mSubresourceLayouts.get(key); + return state->mSubresourceLayouts.get(key); } #if FVK_ENABLED(FVK_DEBUG_TEXTURE) diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index 6b19a968320..4bdf0c4e230 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -30,20 +30,82 @@ namespace filament::backend { +class VulkanResourceAllocator; + +struct VulkanTextureState : public VulkanResource { + VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, + VulkanStagePool& stagePool, + VkFormat format, VkImageViewType viewType, VkComponentMapping swizzle); + + struct ImageViewKey { + VkImageSubresourceRange range; // 4 * 5 bytes + VkImageViewType type; // 4 bytes + VkComponentMapping swizzle; // 4 * 4 bytes + + bool operator==(ImageViewKey const& k2) const { + auto const& k1 = *this; + return k1.range.aspectMask == k2.range.aspectMask + && k1.range.baseMipLevel == k2.range.baseMipLevel + && k1.range.levelCount == k2.range.levelCount + && k1.range.baseArrayLayer == k2.range.baseArrayLayer + && k1.range.layerCount == k2.range.layerCount && k1.type == k2.type + && k1.swizzle.r == k2.swizzle.r && k1.swizzle.g == k2.swizzle.g + && k1.swizzle.b == k2.swizzle.b && k1.swizzle.a == k2.swizzle.a; + } + }; + // No implicit padding allowed due to it being a hash key. + static_assert(sizeof(ImageViewKey) == 40); + + using ImageViewHash = utils::hash::MurmurHashFn; + + uint32_t refs = 1; + + // The texture with the sidecar owns the sidecar. + std::unique_ptr mSidecarMSAA; + VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE; + + const VkFormat mVkFormat; + const VkImageViewType mViewType; + const VkComponentMapping mSwizzle; + VkImage mTextureImage = VK_NULL_HANDLE; + + // Track the image layout of each subresource using a sparse range map. + utils::RangeMap mSubresourceLayouts; + + std::unordered_map mCachedImageViews; + VulkanStagePool& mStagePool; + VkDevice mDevice; + VmaAllocator mAllocator; + VulkanCommands* mCommands; + std::shared_ptr mTransitionFence; +}; + + struct VulkanTexture : public HwTexture, VulkanResource { // Standard constructor for user-facing textures. VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, - VmaAllocator allocator, VulkanCommands* commands, SamplerType target, uint8_t levels, + VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + SamplerType target, uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false, VkComponentMapping swizzle = {}); // Specialized constructor for internally created textures (e.g. from a swap chain) // The texture will never destroy the given VkImage, but it does manages its subresources. - VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VkImage image, + VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false); + // constructor for creating a texture view + VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, + VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount, + VulkanStagePool& stagePool); + ~VulkanTexture(); // Uploads data into a subregion of a 2D or 3D texture. @@ -52,13 +114,14 @@ struct VulkanTexture : public HwTexture, VulkanResource { // Returns the primary image view, which is used for shader sampling. VkImageView getPrimaryImageView() { - return getImageView(mPrimaryViewRange, mViewType, mSwizzle); + VulkanTextureState* state = getSharedState(); + return getImageView(mPrimaryViewRange, state->mViewType, state->mSwizzle); } - VkImageViewType getViewType() const { return mViewType; } - - // Sets the min/max range of miplevels in the primary image view. - void setPrimaryRange(uint32_t minMiplevel, uint32_t maxMiplevel); + VkImageViewType getViewType() const { + VulkanTextureState const* state = getSharedState(); + return state->mViewType; + } VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; } @@ -79,17 +142,25 @@ struct VulkanTexture : public HwTexture, VulkanResource { // view type. Swizzle option does not matter in this case. VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type); - VkFormat getVkFormat() const { return mVkFormat; } - VkImage getVkImage() const { return mTextureImage; } + VkFormat getVkFormat() const { + VulkanTextureState const* state = getSharedState(); + return state->mVkFormat; + } + VkImage getVkImage() const { + VulkanTextureState const* state = getSharedState(); + return state->mTextureImage; + } VulkanLayout getLayout(uint32_t layer, uint32_t level) const; void setSidecar(VulkanTexture* sidecar) { - mSidecarMSAA.reset(sidecar); + VulkanTextureState* state = getSharedState(); + state->mSidecarMSAA.reset(sidecar); } VulkanTexture* getSidecar() const { - return mSidecarMSAA.get(); + VulkanTextureState const* state = getSharedState(); + return state->mSidecarMSAA.get(); } void transitionLayout(VulkanCommandBuffer* commands, const VkImageSubresourceRange& range, @@ -109,8 +180,9 @@ struct VulkanTexture : public HwTexture, VulkanResource { void setPrimaryLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout); bool transitionReady() { - auto res = !mTransitionFence || mTransitionFence->getStatus() == VK_SUCCESS; - mTransitionFence.reset(); + VulkanTextureState* state = getSharedState(); + auto res = !state->mTransitionFence || state->mTransitionFence->getStatus() == VK_SUCCESS; + state->mTransitionFence.reset(); return res; } @@ -119,26 +191,8 @@ struct VulkanTexture : public HwTexture, VulkanResource { #endif private: - struct ImageViewKey { - VkImageSubresourceRange range; // 4 * 5 bytes - VkImageViewType type; // 4 bytes - VkComponentMapping swizzle; // 4 * 4 bytes - - bool operator==(ImageViewKey const& k2) const { - auto const& k1 = *this; - return k1.range.aspectMask == k2.range.aspectMask - && k1.range.baseMipLevel == k2.range.baseMipLevel - && k1.range.levelCount == k2.range.levelCount - && k1.range.baseArrayLayer == k2.range.baseArrayLayer - && k1.range.layerCount == k2.range.layerCount && k1.type == k2.type - && k1.swizzle.r == k2.swizzle.r && k1.swizzle.g == k2.swizzle.g - && k1.swizzle.b == k2.swizzle.b && k1.swizzle.a == k2.swizzle.a; - } - }; - // No implicit padding allowed due to it being a hash key. - static_assert(sizeof(ImageViewKey) == 40); - - using ImageViewHash = utils::hash::MurmurHashFn; + VulkanTextureState* getSharedState(); + VulkanTextureState const* getSharedState() const; // Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and // swizzle (or not). @@ -148,29 +202,15 @@ struct VulkanTexture : public HwTexture, VulkanResource { void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel); - // The texture with the sidecar owns the sidecar. - std::unique_ptr mSidecarMSAA; - const VkFormat mVkFormat; - const VkImageViewType mViewType; - const VkComponentMapping mSwizzle; - VkImage mTextureImage = VK_NULL_HANDLE; - VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE; + VulkanResourceAllocator* const mAllocator; - // Track the image layout of each subresource using a sparse range map. - utils::RangeMap mSubresourceLayouts; + Handle mState; VkImageSubresourceRange mFullViewRange; // Track the range of subresources that define the "primary" image view, which is the special // image view that gets bound to an actual texture sampler. VkImageSubresourceRange mPrimaryViewRange; - - std::unordered_map mCachedImageViews; - VulkanStagePool& mStagePool; - VkDevice mDevice; - VmaAllocator mAllocator; - VulkanCommands* mCommands; - std::shared_ptr mTransitionFence; }; } // namespace filament::backend diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index 8d49ef2acd5..bb7a83416ae 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -170,7 +170,6 @@ TEST_F(BackendTest, FeedbackLoops) { DescriptorFlags::NONE, 0 }}}); - DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); TrianglePrimitive const triangle(getDriverApi()); @@ -178,17 +177,10 @@ TEST_F(BackendTest, FeedbackLoops) { auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; Handle const texture = api.createTexture( SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage); - api.updateDescriptorSetTexture(descriptorSet, 0, texture, { - .filterMag = SamplerMagFilter::LINEAR, - .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST - }); // Create ubo auto ubuffer = api.createBufferObject(sizeof(MaterialParams), BufferObjectBinding::UNIFORM, BufferUsage::STATIC); - api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams)); - - api.bindDescriptorSet(descriptorSet, 1, {}); // Create a RenderTarget for each miplevel. Handle renderTargets[kNumLevels]; @@ -237,7 +229,16 @@ TEST_F(BackendTest, FeedbackLoops) { const uint32_t sourceLevel = targetLevel - 1; params.viewport.width = kTexWidth >> targetLevel; params.viewport.height = kTexHeight >> targetLevel; - getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel); + + auto textureView = api.createTextureView(texture, sourceLevel, 1); + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + api.updateDescriptorSetTexture(descriptorSet, 0, textureView, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST + }); + api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams)); + api.bindDescriptorSet(descriptorSet, 1, {}); + uploadUniforms(getDriverApi(), ubuffer, { .fbWidth = float(params.viewport.width), .fbHeight = float(params.viewport.height), @@ -246,6 +247,8 @@ TEST_F(BackendTest, FeedbackLoops) { api.beginRenderPass(renderTargets[targetLevel], params); api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); + api.destroyTexture(textureView); + api.destroyDescriptorSet(descriptorSet); } // Upsample passes @@ -256,7 +259,16 @@ TEST_F(BackendTest, FeedbackLoops) { const uint32_t sourceLevel = targetLevel + 1; params.viewport.width = kTexWidth >> targetLevel; params.viewport.height = kTexHeight >> targetLevel; - getDriverApi().setMinMaxLevels(texture, sourceLevel, sourceLevel); + + auto textureView = api.createTextureView(texture, sourceLevel, 1); + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + api.updateDescriptorSetTexture(descriptorSet, 0, textureView, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST + }); + api.updateDescriptorSetBuffer(descriptorSet, 1, ubuffer, 0, sizeof(MaterialParams)); + api.bindDescriptorSet(descriptorSet, 1, {}); + uploadUniforms(getDriverApi(), ubuffer, { .fbWidth = float(params.viewport.width), .fbHeight = float(params.viewport.height), @@ -265,10 +277,10 @@ TEST_F(BackendTest, FeedbackLoops) { api.beginRenderPass(renderTargets[targetLevel], params); api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); + api.destroyTexture(textureView); + api.destroyDescriptorSet(descriptorSet); } - getDriverApi().setMinMaxLevels(texture, 0, 0x7f); - // Read back the render target corresponding to the base level. // // NOTE: Calling glReadPixels on any miplevel other than the base level @@ -285,7 +297,6 @@ TEST_F(BackendTest, FeedbackLoops) { getDriver().purge(); } - api.destroyDescriptorSet(descriptorSet); api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyProgram(program); api.destroySwapChain(swapChain); diff --git a/filament/backend/test/test_MipLevels.cpp b/filament/backend/test/test_MipLevels.cpp index 94944f797ec..e0c41fb1725 100644 --- a/filament/backend/test/test_MipLevels.cpp +++ b/filament/backend/test/test_MipLevels.cpp @@ -75,7 +75,7 @@ namespace test { using namespace filament; using namespace filament::backend; -TEST_F(BackendTest, SetMinMaxLevel) { +TEST_F(BackendTest, TextureViewLod) { auto& api = getDriverApi(); api.startCapture(0); @@ -116,7 +116,9 @@ TEST_F(BackendTest, SetMinMaxLevel) { DescriptorFlags::NONE, 0 }}}); - DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + DescriptorSetHandle descriptorSet[2]; + descriptorSet[0] = api.createDescriptorSet(descriptorSetLayout); + descriptorSet[1] = api.createDescriptorSet(descriptorSetLayout); // Create a texture that has 4 mip levels. Each level is a different color. // Level 0: 128x128 (red) @@ -161,7 +163,7 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Level 1: 64x64 (green) <-- base // Level 2: 32x32 (blue) <--- white triangle rendered // Level 3: 16x16 (yellow) <-- max - api.setMinMaxLevels(texture, 1, 3); + auto texture13 = api.createTextureView(texture, 1, 3); // Render a white triangle into level 2. // We specify mip level 2, because minMaxLevels has no effect when rendering into a texture. @@ -200,11 +202,11 @@ TEST_F(BackendTest, SetMinMaxLevel) { state.rasterState.depthFunc = SamplerCompareFunc::A; state.rasterState.culling = CullingMode::NONE; - api.updateDescriptorSetTexture(descriptorSet, 0, texture, { + api.updateDescriptorSetTexture(descriptorSet[0], 0, texture13, { .filterMag = SamplerMagFilter::NEAREST, .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - api.bindDescriptorSet(descriptorSet, 0, {}); + api.bindDescriptorSet(descriptorSet[0], 0, {}); // Render a triangle to the screen, sampling from mip level 1. // Because the min level is 1, the result color should be the white triangle drawn in the @@ -216,7 +218,13 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Adjust the base mip to 2. // Note that this is done without another call to updateSamplerGroup. - api.setMinMaxLevels(texture, 2, 3); + auto texture22 = api.createTextureView(texture, 2, 2); + + api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, { + .filterMag = SamplerMagFilter::NEAREST, + .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + + api.bindDescriptorSet(descriptorSet[1], 0, {}); // Render a second, smaller, triangle, again sampling from mip level 1. // This triangle should be yellow striped. @@ -241,9 +249,12 @@ TEST_F(BackendTest, SetMinMaxLevel) { // Cleanup. api.destroySwapChain(swapChain); api.destroyRenderTarget(renderTarget); - api.destroyDescriptorSet(descriptorSet); + api.destroyDescriptorSet(descriptorSet[0]); + api.destroyDescriptorSet(descriptorSet[1]); api.destroyDescriptorSetLayout(descriptorSetLayout); api.destroyTexture(texture); + api.destroyTexture(texture13); + api.destroyTexture(texture22); api.destroyProgram(whiteProgram); api.destroyProgram(textureProgram); } diff --git a/filament/docs/Vulkan.md.html b/filament/docs/Vulkan.md.html index 80db49db33e..2a8902f3455 100644 --- a/filament/docs/Vulkan.md.html +++ b/filament/docs/Vulkan.md.html @@ -182,7 +182,7 @@ If a shader samples from a texture whose mipmaps are only partially loaded, we might see validation warnings about how some subresources are in an UNDEFINED layout. However if we are properly using -the `setMinMaxLevels` driver API, then the Vulkan backend will not bind those particular +the `createTextureView` driver API, then the Vulkan backend will not bind those particular subresources, so validation should not complain. (2) **Writeable Color Textures** diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 44a4231604f..7c70df46b88 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -533,14 +533,14 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph auto in = resources.getTexture(data.depth); auto& material = getPostProcessMaterial("mipmapDepth"); FMaterialInstance* const mi = material.getMaterialInstance(mEngine); - mi->setParameter("depth", in, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); // The first mip already exists, so we process n-1 lods for (size_t level = 0; level < levelCount - 1; level++) { auto out = resources.getRenderPassInfo(level); - driver.setMinMaxLevels(in, level, level); + auto th = driver.createTextureView(in, level, 1); + mi->setParameter("depth", th, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); commitAndRender(out, material, driver); + driver.destroyTexture(th); } - driver.setMinMaxLevels(in, 0, levelCount - 1); }); return { depth, structurePass->picking }; @@ -1644,27 +1644,25 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, auto const& material = getPostProcessMaterial("dofMipmap"); FMaterialInstance* const mi = material.getMaterialInstance(mEngine); - mi->setParameter("color", inOutColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->commit(driver); - mi->use(driver); auto const pipeline = material.getPipelineState(mEngine, variant); for (size_t level = 0 ; level < mipmapCount - 1u ; level++) { const float w = FTexture::valueForLevel(level, desc.width); const float h = FTexture::valueForLevel(level, desc.height); - auto const& out = resources.getRenderPassInfo(data.rp[level]); - driver.setMinMaxLevels(inOutColor, level, level); - driver.setMinMaxLevels(inOutCoc, level, level); + auto inColor = driver.createTextureView(inOutColor, level, 1); + auto inCoc = driver.createTextureView(inOutCoc, level, 1); + mi->setParameter("color", inColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("coc", inCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres? mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h }); mi->commit(driver); + mi->use(driver); render(out, pipeline, driver); + driver.destroyTexture(inColor); + driver.destroyTexture(inCoc); } - driver.setMinMaxLevels(inOutColor, 0, mipmapCount - 1u); - driver.setMinMaxLevels(inOutCoc, 0, mipmapCount - 1u); }); /* @@ -2084,17 +2082,6 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg, auto* mi9 = material9.getMaterialInstance(mEngine); auto* mi13 = material13.getMaterialInstance(mEngine); - mi9->setParameter("source", hwOut, { - .filterMag = SamplerMagFilter::LINEAR, - .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - - mi13->setParameter("source", hwOut, { - .filterMag = SamplerMagFilter::LINEAR, - .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - - mi9->commit(driver); - mi13->commit(driver); - // PipelineState for both materials should be the same auto const pipeline = material9.getPipelineState(mEngine); @@ -2107,11 +2094,15 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg, // 9 samples filter. auto vp = resources.getRenderPassInfo(data.outRT[i-1]).params.viewport; auto* const mi = (vp.width & 1 || vp.height & 1) ? mi13 : mi9; + auto hwOutView = driver.createTextureView(hwOut, i - 1, 1); + mi->setParameter("source", hwOutView, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); + mi->commit(driver); mi->use(driver); - driver.setMinMaxLevels(hwOut, i - 1, i - 1); // this offsets baseLevel to i-1 render(hwDstRT, pipeline, driver); + driver.destroyTexture(hwOutView); } - driver.setMinMaxLevels(hwOut, 0, inoutBloomOptions.levels - 1); }); // output of bloom downsample pass becomes input of next (flare) pass @@ -2135,11 +2126,6 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg, auto const& material = getPostProcessMaterial("bloomUpsample"); auto* mi = material.getMaterialInstance(mEngine); - mi->setParameter("source", hwOut, { - .filterMag = SamplerMagFilter::LINEAR, - .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST}); - mi->commit(driver); - mi->use(driver); auto pipeline = material.getPipelineState(mEngine); pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE; @@ -2151,12 +2137,16 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg, hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE; auto w = FTexture::valueForLevel(i - 1, outDesc.width); auto h = FTexture::valueForLevel(i - 1, outDesc.height); + auto hwOutView = driver.createTextureView(hwOut, i, 1); mi->setParameter("resolution", float4{ w, h, 1.0f / w, 1.0f / h }); + mi->setParameter("source", hwOutView, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST}); mi->commit(driver); - driver.setMinMaxLevels(hwOut, i, i); // this offsets baseLevel to i + mi->use(driver); render(hwDstRT, pipeline, driver); + driver.destroyTexture(hwOutView); } - driver.setMinMaxLevels(hwOut, 0, inoutBloomOptions.levels - 1); }); return { bloomUpsamplePass->out, flare }; @@ -2366,7 +2356,7 @@ FrameGraphId PostProcessManager::colorGrading(FrameGraph& fg, bloomStrength = clamp(bloomOptions.strength, 0.0f, 1.0f); if (bloomOptions.dirt) { FTexture* fdirt = downcast(bloomOptions.dirt); - FrameGraphTexture const frameGraphTexture{ .handle = fdirt->getHwHandle() }; + FrameGraphTexture const frameGraphTexture{ .handle = fdirt->getHwHandleForSampling() }; bloomDirt = fg.import("dirt", { .width = (uint32_t)fdirt->getWidth(0u), .height = (uint32_t)fdirt->getHeight(0u), @@ -3260,7 +3250,7 @@ FrameGraphId PostProcessManager::resolveDepth(FrameGraph& fg, FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg, FrameGraphId input, uint8_t layer, size_t level, - math::float4 clearColor, bool finalize) noexcept { + math::float4 clearColor) noexcept { struct VsmMipData { FrameGraphId in; @@ -3284,7 +3274,7 @@ FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { - auto in = resources.getTexture(data.in); + auto in = driver.createTextureView(resources.getTexture(data.in), level, 1); auto out = resources.getRenderPassInfo(); auto const& inDesc = resources.getDescriptor(data.in); @@ -3292,8 +3282,6 @@ FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg assert_invariant(width == inDesc.height); int const dim = width >> (level + 1); - driver.setMinMaxLevels(in, level, level); - auto& material = getPostProcessMaterial("vsmMipmap"); // When generating shadow map mip levels, we want to preserve the 1 texel border. @@ -3311,10 +3299,7 @@ FrameGraphId PostProcessManager::vsmMipmapPass(FrameGraph& fg mi->commit(driver); mi->use(driver); render(out, pipeline, scissor, driver); - - if (finalize) { - driver.setMinMaxLevels(in, 0, level); - } + driver.destroyTexture(in); // `in` is just a view on `data.in` }); return depthMipmapPass->in; diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 7bed969cc99..e3db6fd9945 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -296,7 +296,7 @@ class PostProcessManager { // VSM shadow mipmap pass FrameGraphId vsmMipmapPass(FrameGraph& fg, FrameGraphId input, uint8_t layer, size_t level, - math::float4 clearColor, bool finalize) noexcept; + math::float4 clearColor) noexcept; FrameGraphId gaussianBlurPass(FrameGraph& fg, FrameGraphId input, diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index e48b5203860..3c512284c0d 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -518,9 +518,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG // So generate the mipmaps for each layer if (textureRequirements.levels > 1) { for (size_t level = 0; level < textureRequirements.levels - 1; level++) { - const bool finalize = level == textureRequirements.levels - 2; - ppm.vsmMipmapPass(fg, prepareShadowPass->shadows, layer, level, - vsmClearColor, finalize); + ppm.vsmMipmapPass(fg, prepareShadowPass->shadows, layer, level, vsmClearColor); } } } diff --git a/filament/src/details/IndirectLight.cpp b/filament/src/details/IndirectLight.cpp index 2822266c72b..6d78d564088 100644 --- a/filament/src/details/IndirectLight.cpp +++ b/filament/src/details/IndirectLight.cpp @@ -200,11 +200,13 @@ void FIndirectLight::terminate(FEngine& engine) { } backend::Handle FIndirectLight::getReflectionHwHandle() const noexcept { - return mReflectionsTexture ? mReflectionsTexture->getHwHandle() : backend::Handle {}; + return mReflectionsTexture ? mReflectionsTexture->getHwHandleForSampling() + : backend::Handle{}; } backend::Handle FIndirectLight::getIrradianceHwHandle() const noexcept { - return mIrradianceTexture ? mIrradianceTexture->getHwHandle() : backend::Handle {}; + return mIrradianceTexture ? mIrradianceTexture->getHwHandleForSampling() + : backend::Handle{}; } math::float3 FIndirectLight::getDirectionEstimate(math::float3 const* f) noexcept { diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index a9ae242d4a9..812e0dbac49 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -27,12 +27,15 @@ #include "private/filament/EngineEnums.h" +#include #include #include #include +#include #include +#include #include #include #include @@ -176,6 +179,14 @@ void FMaterialInstance::commit(DriverApi& driver) const { if (mUniforms.isDirty()) { driver.updateBufferObject(mUbHandle, mUniforms.toBufferDescriptor(driver), 0); } + if (!mTextureParameters.empty()) { + for (auto const& [binding, p]: mTextureParameters) { + assert_invariant(p.texture); + Handle handle = p.texture->getHwHandleForSampling(); + assert_invariant(handle); + mDescriptorSet.setSampler(binding, handle, p.params); + } + } // Commit descriptors if needed (e.g. when textures are updated,or the first time) mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver); } @@ -205,17 +216,25 @@ void FMaterialInstance::setParameterImpl(std::string_view name, PANIC_LOG("Depth textures can't be sampled with a linear filter " "unless the comparison mode is set to COMPARE_TO_TEXTURE. " "(material: \"%s\", parameter: \"%.*s\")", - getMaterial()->getName().c_str(), name.size(), name.data()); + getMaterial()->getName().c_str(), name.size(), name.data()); } } } #endif - Handle handle{}; - if (UTILS_LIKELY(texture)) { - handle = texture->getHwHandle(); + auto binding = mMaterial->getSamplerBinding(name); + if (texture && texture->canHaveTextureView()) { + mTextureParameters[binding] = { texture, sampler.getSamplerParams() }; + } else { + Handle handle{}; + if (texture) { + handle = texture->getHwHandleForSampling(); + assert_invariant(handle == texture->getHwHandle()); + } else { + mTextureParameters.erase(binding); + } + mDescriptorSet.setSampler(binding, handle, sampler.getSamplerParams()); } - setParameter(name, handle, sampler.getSamplerParams()); } void FMaterialInstance::setMaskThreshold(float threshold) noexcept { diff --git a/filament/src/details/MaterialInstance.h b/filament/src/details/MaterialInstance.h index 985555b42e7..5e16e2505d7 100644 --- a/filament/src/details/MaterialInstance.h +++ b/filament/src/details/MaterialInstance.h @@ -29,11 +29,14 @@ #include +#include #include #include #include +#include + #include #include #include @@ -45,6 +48,7 @@ namespace filament { class FMaterial; +class FTexture; class FMaterialInstance : public MaterialInstance { public: @@ -238,7 +242,13 @@ class FMaterialInstance : public MaterialInstance { // keep these grouped, they're accessed together in the render-loop FMaterial const* mMaterial = nullptr; + struct TextureParameter { + FTexture const* texture; + backend::SamplerParams params; + }; + backend::Handle mUbHandle; + tsl::robin_map mTextureParameters; mutable filament::DescriptorSet mDescriptorSet; UniformBuffer mUniforms; diff --git a/filament/src/details/RenderTarget.cpp b/filament/src/details/RenderTarget.cpp index dad0af4d633..131420b4ef6 100644 --- a/filament/src/details/RenderTarget.cpp +++ b/filament/src/details/RenderTarget.cpp @@ -121,7 +121,8 @@ FRenderTarget::FRenderTarget(FEngine& engine, const RenderTarget::Builder& build backend::MRT mrt{}; TargetBufferInfo dinfo{}; - auto setAttachment = [this](TargetBufferInfo& info, AttachmentPoint attachmentPoint) { + auto setAttachment = [this, &driver = engine.getDriverApi()] + (TargetBufferInfo& info, AttachmentPoint attachmentPoint) { Attachment const& attachment = mAttachments[(size_t)attachmentPoint]; auto t = downcast(attachment.texture); info.handle = t->getHwHandle(); @@ -131,6 +132,7 @@ FRenderTarget::FRenderTarget(FEngine& engine, const RenderTarget::Builder& build } else { info.layer = attachment.layer; } + t->updateLodRange(info.level); }; UTILS_NOUNROLL diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 3734df4b6d9..ba1689d65f4 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -221,6 +222,7 @@ Texture* Texture::Builder::build(Engine& engine) { FTexture::FTexture(FEngine& engine, const Builder& builder) { FEngine::DriverApi& driver = engine.getDriverApi(); + mDriver = &driver; // this is unfortunately needed for getHwHandleForSampling() mWidth = static_cast(builder->mWidth); mHeight = static_cast(builder->mHeight); mDepth = static_cast(builder->mDepth); @@ -243,11 +245,15 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { mHandle = driver.importTexture(builder->mImportedId, mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage); } + mHandleForSampling = mHandle; } // frees driver resources, object becomes invalid void FTexture::terminate(FEngine& engine) { FEngine::DriverApi& driver = engine.getDriverApi(); + if (mHandleForSampling != mHandle) { + driver.destroyTexture(mHandleForSampling); + } driver.destroyTexture(mHandle); } @@ -349,6 +355,9 @@ void FTexture::setImage(FEngine& engine, size_t level, engine.getDriverApi().update3DImage(mHandle, uint8_t(level), xoffset, yoffset, zoffset, width, height, depth, std::move(p)); + + // this method shouldn't have been const + const_cast(this)->updateLodRange(level); } // deprecated @@ -415,6 +424,9 @@ void FTexture::setImage(FEngine& engine, size_t level, engine.getDriverApi().queueCommand( make_copyable_function([buffer = std::move(buffer)]() {})); } + + // this method shouldn't been const + const_cast(this)->updateLodRange(level); } void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { @@ -464,6 +476,58 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept { } engine.getDriverApi().generateMipmaps(mHandle); + // this method shouldn't have been const + const_cast(this)->updateLodRange(0, mLevelCount); +} + +bool FTexture::canHaveTextureView() const noexcept { + // TODO: this will eventually include swizzling + return any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1; +} + +void FTexture::updateLodRange(uint8_t baseLevel, uint8_t levelCount) noexcept { + if (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) { + auto& range = mLodRange; + uint8_t const last = int8_t(baseLevel + levelCount); + if (range.first > baseLevel || range.last < last) { + if (range.empty()) { + range = { baseLevel, last }; + } else { + range.first = std::min(range.first, baseLevel); + range.last = std::max(range.last, last); + } + if (range.first == 0 && range.last == mLevelCount) { + // the whole range lod range is used, we don't need the view anymore + range.first = range.last = 0; + } + // We defer the creation of the texture view to getHwHandleForSampling() because it + // is a common case that by then, the view won't be needed. Creating the first view on a + // texture has a backend cost. + } + } +} + +backend::Handle FTexture::getHwHandleForSampling() const noexcept { + auto const& range = mLodRange; + auto& activeRange = mActiveLodRange; + if (UTILS_UNLIKELY(activeRange.first != range.first || activeRange.last != range.last)) { + activeRange = range; + if (mHandleForSampling != mHandle) { + mDriver->destroyTexture(mHandleForSampling); + } + if (range.empty()) { + mHandleForSampling = mHandle; + } else { + mHandleForSampling = mDriver->createTextureView(mHandle, + range.first, + range.last - range.first); + } + } + return mHandleForSampling; +} + +void FTexture::updateLodRange(uint8_t level) noexcept { + updateLodRange(level, 1); } bool FTexture::isTextureFormatSupported(FEngine& engine, InternalFormat format) noexcept { diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index 0e0fe9320ee..7c49ea746be 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -19,12 +19,17 @@ #include "downcast.h" +#include +#include #include #include #include +#include +#include + namespace filament { class FEngine; @@ -38,6 +43,7 @@ class FTexture : public Texture { void terminate(FEngine& engine); backend::Handle getHwHandle() const noexcept { return mHandle; } + backend::Handle getHwHandleForSampling() const noexcept; size_t getWidth(size_t level = 0) const noexcept; size_t getHeight(size_t level = 0) const noexcept; @@ -114,10 +120,25 @@ class FTexture : public Texture { static bool validatePixelFormatAndType(backend::TextureFormat internalFormat, backend::PixelDataFormat format, backend::PixelDataType type) noexcept; + bool canHaveTextureView() const noexcept; + void updateLodRange(uint8_t level) noexcept; + private: friend class Texture; - FStream* mStream = nullptr; + struct LodRange { + // 0,0 means lod-range unset (all levels are available) + uint8_t first = 0; // first lod + uint8_t last = 0; // 1 past last lod + bool empty() const noexcept { return first == last; } + }; + void updateLodRange(uint8_t baseLevel, uint8_t levelCount) noexcept; + backend::Handle mHandle; + mutable backend::Handle mHandleForSampling; + backend::DriverApi* mDriver = nullptr; // this is only needed for getHwHandleForSampling() + LodRange mLodRange{}; + mutable LodRange mActiveLodRange{}; + uint32_t mWidth = 1; uint32_t mHeight = 1; uint32_t mDepth = 1; @@ -125,10 +146,12 @@ class FTexture : public Texture { Sampler mTarget = Sampler::SAMPLER_2D; uint8_t mLevelCount = 1; uint8_t mSampleCount = 1; + Usage mUsage = Usage::DEFAULT; + // there is 7 bytes of padding here + FStream* mStream = nullptr; // only needed for streaming textures }; - FILAMENT_DOWNCAST(Texture) } // namespace filament diff --git a/filament/src/ds/ColorPassDescriptorSet.cpp b/filament/src/ds/ColorPassDescriptorSet.cpp index 326db28563d..f64739df454 100644 --- a/filament/src/ds/ColorPassDescriptorSet.cpp +++ b/filament/src/ds/ColorPassDescriptorSet.cpp @@ -259,7 +259,7 @@ void ColorPassDescriptorSet::prepareFog(FEngine& engine, const CameraInfo& camer // currently they're inferred. Handle fogColorTextureHandle; if (options.skyColor) { - fogColorTextureHandle = downcast(options.skyColor)->getHwHandle(); + fogColorTextureHandle = downcast(options.skyColor)->getHwHandleForSampling(); math::half2 const minMaxMip{ 0.0f, float(options.skyColor->getLevels()) - 1.0f }; s.fogMinMaxMip = packHalf2x16(minMaxMip); s.fogOneOverFarMinusNear = 1.0f / (cameraInfo.zf - cameraInfo.zn); @@ -282,7 +282,8 @@ void ColorPassDescriptorSet::prepareFog(FEngine& engine, const CameraInfo& camer } setSampler(+PerViewBindingPoints::FOG, - fogColorTextureHandle ? fogColorTextureHandle : engine.getDummyCubemap()->getHwHandle(), { + fogColorTextureHandle ? + fogColorTextureHandle : engine.getDummyCubemap()->getHwHandleForSampling(), { .filterMag = SamplerMagFilter::LINEAR, .filterMin = SamplerMinFilter::LINEAR_MIPMAP_LINEAR }); From d8580d93ada6691b97c28465f9b8a0587d723437 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Tue, 23 Jul 2024 13:06:48 -0600 Subject: [PATCH 17/49] New external image API (#7981) --- .../include/private/backend/DriverAPI.inc | 17 +++++++ filament/backend/src/metal/MetalDriver.mm | 34 +++++++++++++- filament/backend/src/opengl/OpenGLDriver.cpp | 21 +++++++++ filament/backend/src/vulkan/VulkanDriver.cpp | 17 +++++++ filament/src/details/MaterialInstance.cpp | 2 +- filament/src/details/Texture.cpp | 45 ++++++++++++++----- filament/src/details/Texture.h | 2 +- 7 files changed, 123 insertions(+), 15 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 924a1f92ad0..16a50e60090 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -215,6 +215,21 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureSwizzled, backend::TextureSwizzle, b, backend::TextureSwizzle, a) +DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage, + backend::TextureFormat, format, + uint32_t, width, + uint32_t, height, + backend::TextureUsage, usage, + void*, image) + +DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImagePlane, + backend::TextureFormat, format, + uint32_t, width, + uint32_t, height, + backend::TextureUsage, usage, + void*, image, + uint32_t, plane) + DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture, intptr_t, id, backend::SamplerType, target, @@ -393,10 +408,12 @@ DECL_DRIVER_API_N(update3DImage, DECL_DRIVER_API_N(generateMipmaps, backend::TextureHandle, th) +// Deprecated DECL_DRIVER_API_N(setExternalImage, backend::TextureHandle, th, void*, image) +// Deprecated DECL_DRIVER_API_N(setExternalImagePlane, backend::TextureHandle, th, void*, image, diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 871ef482d96..8cf5d34f568 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -347,7 +347,16 @@ auto* descriptorSet = handle_cast(dsh); auto* texture = handle_cast(th); - id mtlTexture = texture->getMtlTextureForRead(); + + id mtlTexture = nil; + if (texture->target == SamplerType::SAMPLER_EXTERNAL) { + // TODO: support Metal external images and descriptor sets + mtlTexture = getOrCreateEmptyTexture(mContext); + } else { + mtlTexture = texture->getMtlTextureForRead(); + } + assert_invariant(mtlTexture != nil); + descriptorSet->textures[binding] = MetalDescriptorSet::TextureBinding { mtlTexture, params }; auto const& bindings = descriptorSet->layout->getBindings(); @@ -445,6 +454,21 @@ src, baseLevel, levelCount)); } +void MetalDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, + uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { + createTextureR( + th, SamplerType::SAMPLER_EXTERNAL, 1, TextureFormat::RGBA8, 1, width, height, 1, usage); + setExternalImage(th, image); +} + +void MetalDriver::createTextureExternalImagePlaneR(Handle th, + backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, + void* image, uint32_t plane) { + createTextureR( + th, SamplerType::SAMPLER_EXTERNAL, 1, TextureFormat::RGBA8, 1, width, height, 1, usage); + setExternalImagePlane(th, image, plane); +} + void MetalDriver::importTextureR(Handle th, intptr_t i, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, @@ -651,6 +675,14 @@ return alloc_handle(); } +Handle MetalDriver::createTextureExternalImageS() noexcept { + return alloc_handle(); +} + +Handle MetalDriver::createTextureExternalImagePlaneS() noexcept { + return alloc_handle(); +} + Handle MetalDriver::importTextureS() noexcept { return alloc_handle(); } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 0db682a0b3d..4c0f2caaec3 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -518,6 +518,14 @@ Handle OpenGLDriver::createTextureViewS() noexcept { return initHandle(); } +Handle OpenGLDriver::createTextureExternalImageS() noexcept { + return initHandle(); +} + +Handle OpenGLDriver::createTextureExternalImagePlaneS() noexcept { + return initHandle(); +} + Handle OpenGLDriver::importTextureS() noexcept { return initHandle(); } @@ -953,6 +961,19 @@ void OpenGLDriver::createTextureViewR(Handle th, CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, + uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { + createTextureR(th, SamplerType::SAMPLER_EXTERNAL, 1, format, 1, width, height, 1, usage); + setExternalImage(th, image); +} + +void OpenGLDriver::createTextureExternalImagePlaneR(Handle th, + backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, + void* image, uint32_t plane) { + createTextureR(th, SamplerType::SAMPLER_EXTERNAL, 1, format, 1, width, height, 1, usage); + setExternalImagePlane(th, image, plane); +} + void OpenGLDriver::importTextureR(Handle th, intptr_t id, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) { diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 9fcbc1710e5..1768a02d8e7 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -581,6 +581,15 @@ void VulkanDriver::createTextureViewR(Handle th, mResourceManager.acquire(vktexture); } +void VulkanDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, + uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { +} + +void VulkanDriver::createTextureExternalImagePlaneR(Handle th, + backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, + void* image, uint32_t plane) { +} + void VulkanDriver::importTextureR(Handle th, intptr_t id, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, @@ -767,6 +776,14 @@ Handle VulkanDriver::createTextureViewS() noexcept { return mResourceAllocator.allocHandle(); } +Handle VulkanDriver::createTextureExternalImageS() noexcept { + return mResourceAllocator.allocHandle(); +} + +Handle VulkanDriver::createTextureExternalImagePlaneS() noexcept { + return mResourceAllocator.allocHandle(); +} + Handle VulkanDriver::importTextureS() noexcept { return mResourceAllocator.allocHandle(); } diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index 812e0dbac49..c635fb16529 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -223,7 +223,7 @@ void FMaterialInstance::setParameterImpl(std::string_view name, #endif auto binding = mMaterial->getSamplerBinding(name); - if (texture && texture->canHaveTextureView()) { + if (texture && texture->textureHandleCanMutate()) { mTextureParameters[binding] = { texture, sampler.getSamplerParams() }; } else { Handle handle{}; diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index ba1689d65f4..0e6b3d5c8b0 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -231,6 +231,11 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { mTarget = builder->mTarget; mLevelCount = builder->mLevels; + if (mTarget == SamplerType::SAMPLER_EXTERNAL) { + // mHandle and mHandleForSampling will be created in setExternalImage() + return; + } + if (UTILS_LIKELY(builder->mImportedId == 0)) { if (UTILS_LIKELY(!builder->mTextureIsSwizzled)) { mHandle = driver.createTexture( @@ -430,21 +435,35 @@ void FTexture::setImage(FEngine& engine, size_t level, } void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { - if (mTarget == Sampler::SAMPLER_EXTERNAL) { - // The call to setupExternalImage is synchronous, and allows the driver to take ownership of - // the external image on this thread, if necessary. - engine.getDriverApi().setupExternalImage(image); - engine.getDriverApi().setExternalImage(mHandle, image); + if (mTarget != Sampler::SAMPLER_EXTERNAL) { + return; + } + // The call to setupExternalImage is synchronous, and allows the driver to take ownership of the + // external image on this thread, if necessary. + auto& api = engine.getDriverApi(); + api.setupExternalImage(image); + if (mHandle) { + api.destroyTexture(mHandle); + assert_invariant(mHandleForSampling == mHandle); } + mHandle = api.createTextureExternalImage(mFormat, mWidth, mHeight, mUsage, image); + mHandleForSampling = mHandle; } void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noexcept { - if (mTarget == Sampler::SAMPLER_EXTERNAL) { - // The call to setupExternalImage is synchronous, and allows the driver to take ownership of - // the external image on this thread, if necessary. - engine.getDriverApi().setupExternalImage(image); - engine.getDriverApi().setExternalImagePlane(mHandle, image, plane); + if (mTarget != Sampler::SAMPLER_EXTERNAL) { + return; } + // The call to setupExternalImage is synchronous, and allows the driver to take ownership of + // the external image on this thread, if necessary. + auto& api = engine.getDriverApi(); + api.setupExternalImage(image); + if (mHandle) { + api.destroyTexture(mHandle); + assert_invariant(mHandleForSampling == mHandle); + } + mHandle = api.createTextureExternalImagePlane(mFormat, mWidth, mHeight, mUsage, image, plane); + mHandleForSampling = mHandle; } void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept { @@ -480,12 +499,14 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept { const_cast(this)->updateLodRange(0, mLevelCount); } -bool FTexture::canHaveTextureView() const noexcept { +bool FTexture::textureHandleCanMutate() const noexcept { // TODO: this will eventually include swizzling - return any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1; + return (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) || + mTarget == SamplerType::SAMPLER_EXTERNAL; } void FTexture::updateLodRange(uint8_t baseLevel, uint8_t levelCount) noexcept { + assert_invariant(mTarget != SamplerType::SAMPLER_EXTERNAL); if (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) { auto& range = mLodRange; uint8_t const last = int8_t(baseLevel + levelCount); diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index 7c49ea746be..96c3f380bd2 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -120,7 +120,7 @@ class FTexture : public Texture { static bool validatePixelFormatAndType(backend::TextureFormat internalFormat, backend::PixelDataFormat format, backend::PixelDataType type) noexcept; - bool canHaveTextureView() const noexcept; + bool textureHandleCanMutate() const noexcept; void updateLodRange(uint8_t level) noexcept; private: From bb1f58e97c663cea78655a89fb50d17c2a0366b7 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 31 Jul 2024 10:40:09 +0800 Subject: [PATCH 18/49] Fix broken merge for vk (#8007) --- filament/backend/src/vulkan/VulkanDriver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 8a985676e2b..3fc374c67c8 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -685,10 +685,10 @@ void VulkanDriver::createRenderTargetR(Handle rth, assert_invariant(tmin == tmax); assert_invariant(tmin.x >= width && tmin.y >= height); - auto renderTarget = mResourceAllocator.construct(rth, mPlatform->getDevice(), - - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, width, height, - samples, colorTargets, depthStencil, mStagePool, layerCount); + auto renderTarget = mResourceAllocator.construct(rth, + mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, + &mCommands, &mResourceAllocator, width, height, samples, colorTargets, depthStencil, + mStagePool, layerCount); mResourceManager.acquire(renderTarget); } From 9401d6be9ec8f732a1a514fe5386a425070d383c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 11 Aug 2024 23:53:16 -0700 Subject: [PATCH 19/49] fix warning about missing case this is to allow -Werror to work. --- libs/filamat/src/GLSLPostProcessor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 6853c911a42..134087a45eb 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -556,8 +556,10 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, "spvDescriptorSetBuffer" + std::to_string(int(setIndex))); for (auto const& descriptor : descriptors) { auto const& [name, info, sampler] = descriptor; - // TODO: Handle INPUT_ATTACHMENT case switch (info.type) { + case DescriptorType::INPUT_ATTACHMENT: + // TODO: Handle INPUT_ATTACHMENT case + break; case DescriptorType::UNIFORM_BUFFER: case DescriptorType::SHADER_STORAGE_BUFFER: { std::string lowercasedName = name.c_str(); From d073fb534230dac25caab5ec9cb4ca7552e87957 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Thu, 15 Aug 2024 17:24:01 -0700 Subject: [PATCH 20/49] Implement external images / descriptor sets for Metal --- filament/backend/src/metal/MetalContext.h | 8 +- filament/backend/src/metal/MetalContext.mm | 8 +- filament/backend/src/metal/MetalDriver.h | 12 + filament/backend/src/metal/MetalDriver.mm | 94 +++++--- .../backend/src/metal/MetalExternalImage.h | 105 ++++---- .../backend/src/metal/MetalExternalImage.mm | 226 ++++++++---------- filament/backend/src/metal/MetalHandles.h | 16 +- filament/backend/src/metal/MetalHandles.mm | 40 ++-- .../backend/test/test_RenderExternalImage.cpp | 18 +- 9 files changed, 276 insertions(+), 251 deletions(-) diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index d0cf850be40..73e674d11b8 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -126,8 +126,12 @@ struct MetalContext { id device = nullptr; id commandQueue = nullptr; - id pendingCommandBuffer = nullptr; - id currentRenderPassEncoder = nullptr; + // The ID of pendingCommandBuffer (or the next command buffer, if pendingCommandBuffer is nil). + uint64_t pendingCommandBufferId = 1; + // read from driver thread, set from completion handlers + std::atomic latestCompletedCommandBufferId = 0; + id pendingCommandBuffer = nil; + id currentRenderPassEncoder = nil; std::atomic memorylessLimitsReached = false; diff --git a/filament/backend/src/metal/MetalContext.mm b/filament/backend/src/metal/MetalContext.mm index 077a999b715..e2348388f3b 100644 --- a/filament/backend/src/metal/MetalContext.mm +++ b/filament/backend/src/metal/MetalContext.mm @@ -101,9 +101,14 @@ void initializeSupportedGpuFamilies(MetalContext* context) { context->pendingCommandBuffer = [context->commandQueue commandBuffer]; // It's safe for this block to capture the context variable. MetalDriver::terminate will ensure // all frames and their completion handlers finish before context is deallocated. + uint64_t thisCommandBufferId = context->pendingCommandBufferId; [context->pendingCommandBuffer addCompletedHandler:^(id buffer) { context->resourceTracker.clearResources((__bridge void*) buffer); - + + // Command buffers should complete in order, so latestCompletedCommandBufferId will only + // ever increase. + context->latestCompletedCommandBufferId = thisCommandBufferId; + auto errorCode = (MTLCommandBufferError)buffer.error.code; if (@available(macOS 11.0, *)) { if (errorCode == MTLCommandBufferErrorMemoryless) { @@ -125,6 +130,7 @@ void submitPendingCommands(MetalContext* context) { assert_invariant(context->pendingCommandBuffer.status != MTLCommandBufferStatusCommitted); [context->pendingCommandBuffer commit]; context->pendingCommandBuffer = nil; + context->pendingCommandBufferId++; } id getOrCreateEmptyTexture(MetalContext* context) { diff --git a/filament/backend/src/metal/MetalDriver.h b/filament/backend/src/metal/MetalDriver.h index 5185046c62a..359ef53d887 100644 --- a/filament/backend/src/metal/MetalDriver.h +++ b/filament/backend/src/metal/MetalDriver.h @@ -32,6 +32,7 @@ #include #include #include +#include namespace filament { namespace backend { @@ -79,6 +80,17 @@ class MetalDriver final : public DriverBase { std::vector> mTickOps; std::mutex mTickOpsLock; + // Tasks regularly executed on the driver thread after a command buffer has completed + struct DeferredTask { + DeferredTask(uint64_t commandBufferId, utils::Invocable&& fn) noexcept + : commandBufferId(commandBufferId), fn(std::move(fn)) {} + uint64_t commandBufferId; // after this command buffer completes + utils::Invocable fn; // execute this task + }; + void executeAfterCurrentCommandBufferCompletes(utils::Invocable&& fn) noexcept; + void executeDeferredOps() noexcept; + std::deque mDeferredTasks; + /* * Driver interface */ diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index ab45338a359..d80e9e22b09 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -235,6 +235,7 @@ void MetalDriver::tick(int) { executeTickOps(); + executeDeferredOps(); } void MetalDriver::beginFrame(int64_t monotonic_clock_ns, @@ -350,12 +351,11 @@ auto* descriptorSet = handle_cast(dsh); auto* texture = handle_cast(th); - id mtlTexture = nil; + id mtlTexture = texture->getMtlTextureForRead(); if (texture->target == SamplerType::SAMPLER_EXTERNAL) { - // TODO: support Metal external images and descriptor sets - mtlTexture = getOrCreateEmptyTexture(mContext); - } else { - mtlTexture = texture->getMtlTextureForRead(); + auto externalImage = texture->getExternalImage(); + assert_invariant(externalImage != nil); + descriptorSet->externalImages.push_back(externalImage); } assert_invariant(mtlTexture != nil); @@ -386,9 +386,13 @@ << "finish must be called outside of a render pass."; // Wait for all frames to finish by submitting and waiting on a dummy command buffer. submitPendingCommands(mContext); - id oneOffBuffer = [mContext->commandQueue commandBuffer]; - [oneOffBuffer commit]; + + id oneOffBuffer = getPendingCommandBuffer(mContext); + submitPendingCommands(mContext); [oneOffBuffer waitUntilCompleted]; + + executeTickOps(); + executeDeferredOps(); } void MetalDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount, @@ -458,17 +462,21 @@ void MetalDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { - createTextureR( - th, SamplerType::SAMPLER_EXTERNAL, 1, TextureFormat::RGBA8, 1, width, height, 1, usage); - setExternalImage(th, image); + mContext->textures.insert(construct_handle( + th, *mContext, format, width, height, usage, (CVPixelBufferRef)image)); + // This release matches the retain call in setupExternalImage. The MetalTexture will have + // retained the buffer by now. + CVPixelBufferRelease((CVPixelBufferRef)image); } void MetalDriver::createTextureExternalImagePlaneR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image, uint32_t plane) { - createTextureR( - th, SamplerType::SAMPLER_EXTERNAL, 1, TextureFormat::RGBA8, 1, width, height, 1, usage); - setExternalImagePlane(th, image, plane); + mContext->textures.insert(construct_handle( + th, *mContext, format, width, height, usage, (CVPixelBufferRef)image, plane)); + // This release matches the retain call in setupExternalImage. The MetalTexture will have + // retained the buffer by now. + CVPixelBufferRelease((CVPixelBufferRef)image); } void MetalDriver::importTextureR(Handle th, intptr_t i, @@ -570,6 +578,9 @@ if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) { CVPixelBufferRef pixelBuffer = (CVPixelBufferRef) nativeWindow; construct_handle(sch, *mContext, pixelBuffer, flags); + // This release matches the retain call in setupExternalImage. The MetalSwapchain will have + // retained the buffer by now. + CVPixelBufferRelease((CVPixelBufferRef)pixelBuffer); } else { auto* metalLayer = (__bridge CAMetalLayer*) nativeWindow; construct_handle(sch, *mContext, metalLayer, flags); @@ -832,7 +843,16 @@ void MetalDriver::destroySwapChain(Handle sch) { if (sch) { - destruct_handle(sch); + auto* swapChain = handle_cast(sch); + // If the SwapChain is a pixel buffer, we need to wait for the current command buffer to + // complete before destroying it. This is because pixel buffer SwapChains hold a + // MetalExternalImage that could still being rendered into. + if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) { + executeAfterCurrentCommandBufferCompletes( + [this, sch]() mutable { destruct_handle(sch); }); + } else { + destruct_handle(sch); + } } } @@ -856,7 +876,8 @@ void MetalDriver::destroyDescriptorSet(Handle dsh) { DEBUG_LOG("[DS] destroyDescriptorSet(dsh = %d)\n", dsh.getId()); if (dsh) { - destruct_handle(dsh); + executeAfterCurrentCommandBufferCompletes( + [this, dsh]() mutable { destruct_handle(dsh); }); } } @@ -871,8 +892,6 @@ // This must be done before calling bufferPool->reset() to ensure no buffers are in flight. finish(); - executeTickOps(); - mContext->bufferPool->reset(); mContext->commandQueue = nil; @@ -1143,27 +1162,18 @@ } void MetalDriver::setupExternalImage(void* image) { - // This is called when passing in a CVPixelBuffer as either an external image or swap chain. - // Here we take ownership of the passed in buffer. It will be released the next time - // setExternalImage is called, when the texture is destroyed, or when the swap chain is - // destroyed. + // setupExternalImage is called on the Filament thread when creating a Texture or SwapChain from + // a CVPixelBuffer external image. Here we take ownership of the passed in buffer by calling + // CVPixelBufferRetain. This keeps the buffer alive until the driver thread processes it (inside + // createTextureExternalImage or createSwapChain), allowing the application to free their + // reference to the buffer. CVPixelBufferRef pixelBuffer = (CVPixelBufferRef) image; CVPixelBufferRetain(pixelBuffer); } -void MetalDriver::setExternalImage(Handle th, void* image) { - FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext)) - << "setExternalImage must be called outside of a render pass."; - auto texture = handle_cast(th); - texture->externalImage.set((CVPixelBufferRef) image); -} +void MetalDriver::setExternalImage(Handle th, void* image) {} -void MetalDriver::setExternalImagePlane(Handle th, void* image, uint32_t plane) { - FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext)) - << "setExternalImagePlane must be called outside of a render pass."; - auto texture = handle_cast(th); - texture->externalImage.set((CVPixelBufferRef) image, plane); -} +void MetalDriver::setExternalImagePlane(Handle th, void* image, uint32_t plane) {} void MetalDriver::setExternalStream(Handle th, Handle sh) { } @@ -1800,6 +1810,8 @@ // External images if (texture->target == SamplerType::SAMPLER_EXTERNAL) { + continue; + /* if (texture->externalImage.isValid()) { id mtlTexture = texture->externalImage.getMetalTextureForDraw(); assert_invariant(mtlTexture); @@ -1809,6 +1821,7 @@ newTextures[binding] = getOrCreateEmptyTexture(mContext); } continue; + */ } newTextures[binding] = texture->getMtlTextureForRead(); @@ -2302,6 +2315,21 @@ } } +void MetalDriver::executeAfterCurrentCommandBufferCompletes(utils::Invocable&& fn) noexcept { + mDeferredTasks.emplace_back(mContext->pendingCommandBufferId, std::move(fn)); +} + +void MetalDriver::executeDeferredOps() noexcept { + for (; !mDeferredTasks.empty(); mDeferredTasks.pop_front()) { + const auto& task = mDeferredTasks.front(); + if (task.commandBufferId <= mContext->latestCompletedCommandBufferId) { + task.fn(); + } else { + break; + } + } +} + // explicit instantiation of the Dispatcher template class ConcreteDispatcher; diff --git a/filament/backend/src/metal/MetalExternalImage.h b/filament/backend/src/metal/MetalExternalImage.h index ad8ef8630b6..c9dc1b91372 100644 --- a/filament/backend/src/metal/MetalExternalImage.h +++ b/filament/backend/src/metal/MetalExternalImage.h @@ -32,100 +32,75 @@ struct MetalContext; * texture. */ class MetalExternalImage { - public: + MetalExternalImage() = default; - MetalExternalImage(MetalContext& context, - TextureSwizzle r = TextureSwizzle::CHANNEL_0, - TextureSwizzle g = TextureSwizzle::CHANNEL_1, - TextureSwizzle b = TextureSwizzle::CHANNEL_2, - TextureSwizzle a = TextureSwizzle::CHANNEL_3) noexcept; + MetalExternalImage(MetalExternalImage&&); + MetalExternalImage& operator=(MetalExternalImage&&); + ~MetalExternalImage() noexcept; - /** - * @return true, if this MetalExternalImage is holding a live external image. Returns false - * until set has been called with a valid CVPixelBuffer. The image can be cleared via - * set(nullptr), and isValid will return false again. - */ - bool isValid() const noexcept; + MetalExternalImage(const MetalExternalImage&) = delete; + MetalExternalImage& operator=(const MetalExternalImage&) = delete; /** - * Set this external image to the passed-in CVPixelBuffer. Future calls to - * getMetalTextureForDraw will return a texture backed by this CVPixelBuffer. Previous - * CVPixelBuffers and related resources will be released when all GPU work using them has - * finished. - * - * Calling set with a YCbCr image will encode a compute pass to convert the image from YCbCr to - * RGB. + * While the texture is used for rendering, this MetalExternalImage must be kept alive. */ - void set(CVPixelBufferRef image) noexcept; + id getMtlTexture() const noexcept; - /** - * Set this external image to a specific plane of the passed-in CVPixelBuffer. Future calls to - * getMetalTextureForDraw will return a texture backed by a single plane of this CVPixelBuffer. - * Previous CVPixelBuffers and related resources will be released when all GPU work using them - * has finished. - */ - void set(CVPixelBufferRef image, size_t plane) noexcept; + bool isValid() const noexcept { + return mImage != nil || mRgbTexture != nullptr; + } - /** - * Returns the width of the external image, or 0 if one is not set. For YCbCr images, returns - * the width of the luminance plane. - */ - size_t getWidth() const noexcept { return mWidth; } + NSUInteger getWidth() const noexcept; + NSUInteger getHeight() const noexcept; /** - * Returns the height of the external image, or 0 if one is not set. For YCbCr images, returns - * the height of the luminance plane. + * Create an external image with the passed-in CVPixelBuffer. + * + * Ownership is taken of the CVPixelBuffer, which will be released when the returned + * MetalExternalImage is destroyed (or, in the case of a YCbCr image, after the conversion has + * completed). + * + * Calling set with a YCbCr image will encode a compute pass to convert the image from + * YCbCr to RGB. */ - size_t getHeight() const noexcept { return mHeight; } + static MetalExternalImage createFromImage(MetalContext& context, CVPixelBufferRef image); /** - * Get a Metal texture used to draw this image and denote that it is used for the current frame. - * For future frames that use this external image, getMetalTextureForDraw must be called again. + * Create an external image with a specific plane of the passed-in CVPixelBuffer. + * + * Ownership is taken of the CVPixelBuffer, which will be released when the returned + * MetalExternalImage is destroyed. */ - id getMetalTextureForDraw() const noexcept; + static MetalExternalImage createFromImagePlane( + MetalContext& context, CVPixelBufferRef image, uint32_t plane); + + static void assertWritableImage(CVPixelBufferRef image); /** * Free resources. Should be called at least once when no further calls to set will occur. */ static void shutdown(MetalContext& context) noexcept; - static void assertWritableImage(CVPixelBufferRef image); - private: + MetalExternalImage(CVPixelBufferRef image, CVMetalTextureRef texture) noexcept + : mImage(image), mTexture(texture) {} + explicit MetalExternalImage(id texture) noexcept : mRgbTexture(texture) {} - void unset(); - - CVMetalTextureRef createTextureFromImage(CVPixelBufferRef image, MTLPixelFormat format, - size_t plane); - id createRgbTexture(size_t width, size_t height); - id createSwizzledTextureView(id texture) const; - id createSwizzledTextureView(CVMetalTextureRef texture) const; - void ensureComputePipelineState(); - id encodeColorConversionPass(id inYPlane, id - inCbCrTexture, id outTexture); + static id createRgbTexture(id device, size_t width, size_t height); + static CVMetalTextureRef createTextureFromImage(CVMetalTextureCacheRef textureCache, + CVPixelBufferRef image, MTLPixelFormat format, size_t plane); + static void ensureComputePipelineState(MetalContext& context); + static id encodeColorConversionPass(MetalContext& context, + id inYPlane, id inCbCrTexture, id outTexture); static constexpr size_t Y_PLANE = 0; static constexpr size_t CBCR_PLANE = 1; - MetalContext& mContext; - - // If the external image has a single plane, mImage and mTexture hold references to the image - // and created Metal texture, respectively. - // mTextureView is a view of mTexture with any swizzling applied. + // TODO: this could probably be a union. CVPixelBufferRef mImage = nullptr; CVMetalTextureRef mTexture = nullptr; - id mTextureView = nullptr; - size_t mWidth = 0; - size_t mHeight = 0; - - // If the external image is in the YCbCr format, this holds the result of the converted RGB - // texture. id mRgbTexture = nil; - - struct { - TextureSwizzle r, g, b, a; - } mSwizzle; }; } // namespace backend diff --git a/filament/backend/src/metal/MetalExternalImage.mm b/filament/backend/src/metal/MetalExternalImage.mm index 518cbbe76df..9ffb587dd1c 100644 --- a/filament/backend/src/metal/MetalExternalImage.mm +++ b/filament/backend/src/metal/MetalExternalImage.mm @@ -34,10 +34,6 @@ namespace filament { namespace backend { -static const auto cvBufferDeleter = [](const void* buffer) { - CVBufferRelease((CVMetalTextureRef) buffer); -}; - static const char* kernel = R"( #include #include @@ -71,18 +67,30 @@ } )"; -MetalExternalImage::MetalExternalImage(MetalContext& context, TextureSwizzle r, TextureSwizzle g, - TextureSwizzle b, TextureSwizzle a) noexcept : mContext(context), mSwizzle{r, g, b, a} { } - -bool MetalExternalImage::isValid() const noexcept { - return mRgbTexture != nil || mImage != nullptr; +NSUInteger MetalExternalImage::getWidth() const noexcept { + if (mImage) { + return CVPixelBufferGetWidth(mImage); + } + if (mRgbTexture) { + return mRgbTexture.width; + } + return 0; } -void MetalExternalImage::set(CVPixelBufferRef image) noexcept { - unset(); +NSUInteger MetalExternalImage::getHeight() const noexcept { + if (mImage) { + return CVPixelBufferGetHeight(mImage); + } + if (mRgbTexture) { + return mRgbTexture.height; + } + return 0; +} +MetalExternalImage MetalExternalImage::createFromImage( + MetalContext& context, CVPixelBufferRef image) { if (!image) { - return; + return {}; } OSType formatType = CVPixelBufferGetPixelFormatType(image); @@ -96,30 +104,29 @@ << "."; if (planeCount == 0) { - mImage = image; - mTexture = createTextureFromImage(image, MTLPixelFormatBGRA8Unorm, 0); - mTextureView = createSwizzledTextureView(mTexture); - mWidth = CVPixelBufferGetWidth(image); - mHeight = CVPixelBufferGetHeight(image); + CVMetalTextureRef texture = + createTextureFromImage(context.textureCache, image, MTLPixelFormatBGRA8Unorm, 0); + return { CVPixelBufferRetain(image), texture }; } if (planeCount == 2) { - CVMetalTextureRef yPlane = createTextureFromImage(image, MTLPixelFormatR8Unorm, Y_PLANE); - CVMetalTextureRef cbcrPlane = createTextureFromImage(image, MTLPixelFormatRG8Unorm, - CBCR_PLANE); + CVPixelBufferRetain(image); + + CVMetalTextureRef yPlane = + createTextureFromImage(context.textureCache, image, MTLPixelFormatR8Unorm, Y_PLANE); + CVMetalTextureRef cbcrPlane = + createTextureFromImage(context.textureCache, image, MTLPixelFormatRG8Unorm, CBCR_PLANE); // Get the size of luminance plane. - mWidth = CVPixelBufferGetWidthOfPlane(image, Y_PLANE); - mHeight = CVPixelBufferGetHeightOfPlane(image, Y_PLANE); + NSUInteger width = CVPixelBufferGetWidthOfPlane(image, Y_PLANE); + NSUInteger height = CVPixelBufferGetHeightOfPlane(image, Y_PLANE); - id rgbTexture = createRgbTexture(mWidth, mHeight); - id commandBuffer = encodeColorConversionPass( + id rgbTexture = createRgbTexture(context.device, width, height); + id commandBuffer = encodeColorConversionPass(context, CVMetalTextureGetTexture(yPlane), CVMetalTextureGetTexture(cbcrPlane), rgbTexture); - mRgbTexture = createSwizzledTextureView(rgbTexture); - [commandBuffer addCompletedHandler:^(id o) { CVBufferRelease(yPlane); CVBufferRelease(cbcrPlane); @@ -127,70 +134,83 @@ }]; [commandBuffer commit]; + return MetalExternalImage { rgbTexture }; } -} -void MetalExternalImage::set(CVPixelBufferRef image, size_t plane) noexcept { - unset(); + return {}; +} +MetalExternalImage MetalExternalImage::createFromImagePlane( + MetalContext& context, CVPixelBufferRef image, uint32_t plane) { if (!image) { - return; + return {}; } const OSType formatType = CVPixelBufferGetPixelFormatType(image); FILAMENT_CHECK_POSTCONDITION(formatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) << "Metal planar external images must be in the 420f format."; - - mImage = image; - - auto getPlaneFormat = [] (size_t plane) { - // Right now Metal only supports kCVPixelFormatType_420YpCbCr8BiPlanarFullRange planar - // external images, so we can make the following assumptions about the format of each plane. - if (plane == 0) { - return MTLPixelFormatR8Unorm; // luminance - } - if (plane == 1) { - // CbCr - return MTLPixelFormatRG8Unorm; // CbCr - } - return MTLPixelFormatInvalid; + FILAMENT_CHECK_POSTCONDITION(plane == 0 || plane == 1) + << "Metal planar external images must be created from planes 0 or 1."; + + auto getPlaneFormat = [](size_t plane) { + // Right now Metal only supports kCVPixelFormatType_420YpCbCr8BiPlanarFullRange planar + // external images, so we can make the following assumptions about the format of each plane. + if (plane == 0) { + return MTLPixelFormatR8Unorm; // luminance + } + if (plane == 1) { + return MTLPixelFormatRG8Unorm; // CbCr + } + return MTLPixelFormatInvalid; }; const MTLPixelFormat format = getPlaneFormat(plane); assert_invariant(format != MTLPixelFormatInvalid); - mTexture = createTextureFromImage(image, format, plane); - mTextureView = createSwizzledTextureView(mTexture); + CVMetalTextureRef mTexture = createTextureFromImage(context.textureCache, image, format, plane); + return { CVPixelBufferRetain(image), mTexture }; +} + +MetalExternalImage::MetalExternalImage(MetalExternalImage&& rhs) { + std::swap(mImage, rhs.mImage); + std::swap(mTexture, rhs.mTexture); + std::swap(mRgbTexture, rhs.mRgbTexture); +} + +MetalExternalImage& MetalExternalImage::operator=(MetalExternalImage&& rhs) { + CVPixelBufferRelease(mImage); + CVBufferRelease(mTexture); + mImage = nullptr; + mTexture = nullptr; + mRgbTexture = nullptr; + std::swap(mImage, rhs.mImage); + std::swap(mTexture, rhs.mTexture); + std::swap(mRgbTexture, rhs.mRgbTexture); + return *this; +} + +MetalExternalImage::~MetalExternalImage() noexcept { + CVPixelBufferRelease(mImage); + CVBufferRelease(mTexture); } -id MetalExternalImage::getMetalTextureForDraw() const noexcept { +id MetalExternalImage::getMtlTexture() const noexcept { if (mRgbTexture) { return mRgbTexture; } - - // Retain the image and Metal texture until the GPU has finished with this frame. This does - // not need to be done for the RGB texture, because it is an Objective-C object whose - // lifetime is automatically managed by Metal. - auto& tracker = mContext.resourceTracker; - auto commandBuffer = getPendingCommandBuffer(&mContext); - if (tracker.trackResource((__bridge void*) commandBuffer, mImage, cvBufferDeleter)) { - CVPixelBufferRetain(mImage); - } - if (tracker.trackResource((__bridge void*) commandBuffer, mTexture, cvBufferDeleter)) { - CVBufferRetain(mTexture); + if (mTexture) { + return CVMetalTextureGetTexture(mTexture); } - - assert_invariant(mTextureView); - return mTextureView; + return nil; } -CVMetalTextureRef MetalExternalImage::createTextureFromImage(CVPixelBufferRef image, - MTLPixelFormat format, size_t plane) { +CVMetalTextureRef MetalExternalImage::createTextureFromImage(CVMetalTextureCacheRef textureCache, + CVPixelBufferRef image, MTLPixelFormat format, size_t plane) { const size_t width = CVPixelBufferGetWidthOfPlane(image, plane); const size_t height = CVPixelBufferGetHeightOfPlane(image, plane); CVMetalTextureRef texture; - CVReturn result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, - mContext.textureCache, image, nullptr, format, width, height, plane, &texture); + CVReturn result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache, + image, nullptr, format, width, height, plane, &texture); FILAMENT_CHECK_POSTCONDITION(result == kCVReturnSuccess) << "Could not create a CVMetalTexture from CVPixelBuffer."; @@ -201,58 +221,19 @@ context.externalImageComputePipelineState = nil; } -void MetalExternalImage::assertWritableImage(CVPixelBufferRef image) { - OSType formatType = CVPixelBufferGetPixelFormatType(image); - FILAMENT_CHECK_PRECONDITION(formatType == kCVPixelFormatType_32BGRA) - << "Metal SwapChain images must be in the 32BGRA format."; -} - -void MetalExternalImage::unset() { - CVPixelBufferRelease(mImage); - CVBufferRelease(mTexture); - - mImage = nullptr; - mTexture = nullptr; - mTextureView = nil; - mRgbTexture = nil; - mWidth = 0; - mHeight = 0; -} - -id MetalExternalImage::createRgbTexture(size_t width, size_t height) { +id MetalExternalImage::createRgbTexture( + id device, size_t width, size_t height) { MTLTextureDescriptor *descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm width:width height:height mipmapped:NO]; descriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead; - return [mContext.device newTextureWithDescriptor:descriptor]; -} - -id MetalExternalImage::createSwizzledTextureView(id texture) const { - const bool isDefaultSwizzle = - mSwizzle.r == TextureSwizzle::CHANNEL_0 && - mSwizzle.g == TextureSwizzle::CHANNEL_1 && - mSwizzle.b == TextureSwizzle::CHANNEL_2 && - mSwizzle.a == TextureSwizzle::CHANNEL_3; - if (!isDefaultSwizzle && mContext.supportsTextureSwizzling) { - // Even though we've already checked supportsTextureSwizzling, we still need to guard these - // calls with @availability, otherwise the API usage will generate compiler warnings. - if (@available(iOS 13, *)) { - texture = createTextureViewWithSwizzle(texture, - getSwizzleChannels(mSwizzle.r, mSwizzle.g, mSwizzle.b, mSwizzle.a)); - } - } - return texture; -} - -id MetalExternalImage::createSwizzledTextureView(CVMetalTextureRef ref) const { - id texture = CVMetalTextureGetTexture(ref); - return createSwizzledTextureView(texture); + return [device newTextureWithDescriptor:descriptor]; } -void MetalExternalImage::ensureComputePipelineState() { - if (mContext.externalImageComputePipelineState != nil) { +void MetalExternalImage::ensureComputePipelineState(MetalContext& context) { + if (context.externalImageComputePipelineState != nil) { return; } @@ -260,29 +241,28 @@ NSString* objcSource = [NSString stringWithCString:kernel encoding:NSUTF8StringEncoding]; - id library = [mContext.device newLibraryWithSource:objcSource - options:nil - error:&error]; + id library = [context.device newLibraryWithSource:objcSource + options:nil + error:&error]; NSERROR_CHECK("Unable to compile Metal shading library."); id kernelFunction = [library newFunctionWithName:@"ycbcrToRgb"]; - mContext.externalImageComputePipelineState = - [mContext.device newComputePipelineStateWithFunction:kernelFunction - error:&error]; + context.externalImageComputePipelineState = + [context.device newComputePipelineStateWithFunction:kernelFunction error:&error]; NSERROR_CHECK("Unable to create Metal compute pipeline state."); } -id MetalExternalImage::encodeColorConversionPass(id inYPlane, - id inCbCrTexture, id outTexture) { - ensureComputePipelineState(); +id MetalExternalImage::encodeColorConversionPass(MetalContext& context, + id inYPlane, id inCbCrTexture, id outTexture) { + ensureComputePipelineState(context); - id commandBuffer = [mContext.commandQueue commandBuffer]; + id commandBuffer = [context.commandQueue commandBuffer]; commandBuffer.label = @"YCbCr to RGB conversion"; id computeEncoder = [commandBuffer computeCommandEncoder]; - [computeEncoder setComputePipelineState:mContext.externalImageComputePipelineState]; + [computeEncoder setComputePipelineState:context.externalImageComputePipelineState]; [computeEncoder setTexture:inYPlane atIndex:0]; [computeEncoder setTexture:inCbCrTexture atIndex:1]; [computeEncoder setTexture:outTexture atIndex:2]; @@ -300,5 +280,11 @@ return commandBuffer; } +void MetalExternalImage::assertWritableImage(CVPixelBufferRef image) { + OSType formatType = CVPixelBufferGetPixelFormatType(image); + FILAMENT_CHECK_PRECONDITION(formatType == kCVPixelFormatType_32BGRA) + << "Metal SwapChain images must be in the 32BGRA format."; +} + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 25de9c935dc..2c108ecd82a 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -85,6 +85,8 @@ class MetalSwapChain : public HwSwapChain { NSUInteger getSurfaceWidth() const; NSUInteger getSurfaceHeight() const; + bool isPixelBuffer() const { return type == SwapChainType::CVPIXELBUFFERREF; } + private: enum class SwapChainType { @@ -94,7 +96,6 @@ class MetalSwapChain : public HwSwapChain { }; bool isCaMetalLayer() const { return type == SwapChainType::CAMETALLAYER; } bool isHeadless() const { return type == SwapChainType::HEADLESS; } - bool isPixelBuffer() const { return type == SwapChainType::CVPIXELBUFFERREF; } void scheduleFrameScheduledCallback(); void scheduleFrameCompletedCallback(); @@ -240,7 +241,11 @@ class MetalTexture : public HwTexture { uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, id texture) noexcept; - ~MetalTexture(); + // Constructors for importing external images. + MetalTexture(MetalContext& context, TextureFormat format, uint32_t width, uint32_t height, + TextureUsage usage, CVPixelBufferRef image) noexcept; + MetalTexture(MetalContext& context, TextureFormat format, uint32_t width, uint32_t height, + TextureUsage usage, CVPixelBufferRef image, uint32_t plane) noexcept; // Returns an id suitable for reading in a shader, taking into account swizzle. id getMtlTextureForRead() const noexcept; @@ -250,13 +255,14 @@ class MetalTexture : public HwTexture { return texture; } + std::shared_ptr getExternalImage() const noexcept { return externalImage; } + void loadImage(uint32_t level, MTLRegion region, PixelBufferDescriptor& p) noexcept; void generateMipmaps() noexcept; static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format); MetalContext& context; - MetalExternalImage externalImage; // A "sidecar" texture used to implement automatic MSAA resolve. // This is created by MetalRenderTarget and stored here so it can be used with multiple @@ -295,6 +301,8 @@ class MetalTexture : public HwTexture { id texture = nil; + std::shared_ptr externalImage; + // If non-nil, a swizzled texture view to use instead of "texture". // Filament swizzling only affects texture reads, so this should not be used when the texture is // bound as a render target attachment. @@ -678,6 +686,8 @@ struct MetalDescriptorSet : public HwDescriptorSet { std::vector> vertexResources; std::vector> fragmentResources; + std::vector> externalImages; + id buffer = nil; }; diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 77ab0be9364..f2ccebdecc2 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -74,7 +74,6 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { depthStencilFormat(decideDepthStencilFormat(flags)), layer(nativeWindow), layerDrawableMutex(std::make_shared()), - externalImage(context), type(SwapChainType::CAMETALLAYER) { if (!(flags & SwapChain::CONFIG_TRANSPARENT) && !nativeWindow.opaque) { @@ -100,17 +99,15 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { depthStencilFormat(decideDepthStencilFormat(flags)), headlessWidth(width), headlessHeight(height), - externalImage(context), type(SwapChainType::HEADLESS) {} MetalSwapChain::MetalSwapChain(MetalContext& context, CVPixelBufferRef pixelBuffer, uint64_t flags) : context(context), depthStencilFormat(decideDepthStencilFormat(flags)), - externalImage(context), + externalImage(MetalExternalImage::createFromImage(context, pixelBuffer)), type(SwapChainType::CVPIXELBUFFERREF) { assert_invariant(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER); MetalExternalImage::assertWritableImage(pixelBuffer); - externalImage.set(pixelBuffer); assert_invariant(externalImage.isValid()); } @@ -121,7 +118,6 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { } MetalSwapChain::~MetalSwapChain() { - externalImage.set(nullptr); } NSUInteger MetalSwapChain::getSurfaceWidth() const { @@ -171,7 +167,7 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { } if (isPixelBuffer()) { - return externalImage.getMetalTextureForDraw(); + return externalImage.getMtlTexture(); } assert_invariant(isCaMetalLayer()); @@ -508,8 +504,7 @@ static void func(void* user) { TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) noexcept - : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context), - externalImage(context, r, g, b, a) { + : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context) { devicePixelFormat = decidePixelFormat(&context, format); FILAMENT_CHECK_POSTCONDITION(devicePixelFormat != MTLPixelFormatInvalid) @@ -619,8 +614,7 @@ static void func(void* user) { MetalTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, src->format, src->usage), - context(context), - externalImage(context) { + context(context) { texture = createTextureViewWithLodRange( src->getMtlTextureForRead(), baseLevel, baseLevel + levelCount - 1); } @@ -628,24 +622,36 @@ static void func(void* user) { MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, id metalTexture) noexcept - : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context), - externalImage(context) { + : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context) { texture = metalTexture; } +MetalTexture::MetalTexture(MetalContext& context, TextureFormat format, uint32_t width, + uint32_t height, TextureUsage usage, CVPixelBufferRef image) noexcept + : HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage), + context(context), + externalImage(std::make_shared( + MetalExternalImage::createFromImage(context, image))) {} + +MetalTexture::MetalTexture(MetalContext& context, TextureFormat format, uint32_t width, + uint32_t height, TextureUsage usage, CVPixelBufferRef image, uint32_t plane) noexcept + : HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage), + context(context), + externalImage(std::make_shared( + MetalExternalImage::createFromImagePlane(context, image, plane))) {} + void MetalTexture::terminate() noexcept { texture = nil; swizzledTextureView = nil; msaaSidecar = nil; - externalImage.set(nullptr); + externalImage = nullptr; terminated = true; } -MetalTexture::~MetalTexture() { - externalImage.set(nullptr); -} - id MetalTexture::getMtlTextureForRead() const noexcept { + if (target == SamplerType::SAMPLER_EXTERNAL) { + return externalImage->getMtlTexture(); + } // TODO: get LODs to work correctly with swizzling return swizzledTextureView ? swizzledTextureView : texture; } diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index 57e138dea16..414e9c414d9 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -149,6 +149,8 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) { api.destroyProgram(program); api.destroyRenderTarget(defaultRenderTarget); + api.finish(); + executeCommands(); } @@ -189,15 +191,6 @@ TEST_F(BackendTest, RenderExternalImage) { // Create a texture that will be backed by an external image. auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE; const NativeView& view = getNativeView(); - backend::Handle texture = api.createTexture( - SamplerType::SAMPLER_EXTERNAL, // target - 1, // levels - TextureFormat::RGBA8, // format - 1, // samples - view.width, // width - view.height, // height - 1, // depth - usage); // usage // Create an external image. CFStringRef keys[4]; @@ -233,7 +226,8 @@ TEST_F(BackendTest, RenderExternalImage) { } api.setupExternalImage(pixBuffer); - api.setExternalImage(texture, pixBuffer); + backend::Handle texture = + api.createTextureExternalImage(TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer); // We're now free to release the buffer. CVBufferRelease(pixBuffer); @@ -266,6 +260,8 @@ TEST_F(BackendTest, RenderExternalImage) { api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1); api.endRenderPass(); + readPixelsAndAssertHash("RenderExternalImage", 512, 512, defaultRenderTarget, 267229901, true); + api.flush(); api.commit(swapChain); api.endFrame(0); @@ -281,6 +277,8 @@ TEST_F(BackendTest, RenderExternalImage) { api.destroyProgram(program); api.destroyRenderTarget(defaultRenderTarget); + api.finish(); + executeCommands(); } From 32ac10161b3838b6278c9ab591f3c15f9cfb7033 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 16 Aug 2024 13:17:37 -0700 Subject: [PATCH 21/49] Move morphing buffer offset out of pipeline check (#8047) The push constant call should not be tied to the caching of the pipeline. --- filament/src/RenderPass.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 8dcc1e2d256..6245905ff9a 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -1043,9 +1043,6 @@ void RenderPass::Executor::execute(FEngine& engine, if (UTILS_UNLIKELY(memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) { currentPipeline = pipeline; driver.bindPipeline(pipeline); - - driver.setPushConstant(ShaderStage::VERTEX, - +PushConstantIds::MORPHING_BUFFER_OFFSET, int32_t(info.morphingOffset)); } if (UTILS_UNLIKELY(info.rph != currentPrimitiveHandle)) { @@ -1063,6 +1060,11 @@ void RenderPass::Executor::execute(FEngine& engine, +DescriptorSetBindingPoints::PER_RENDERABLE, {{ offset, info.skinningOffset }, driver}); + if (UTILS_UNLIKELY(info.hasMorphing)) { + driver.setPushConstant(ShaderStage::VERTEX, + +PushConstantIds::MORPHING_BUFFER_OFFSET, int32_t(info.morphingOffset)); + } + driver.draw2(info.indexOffset, info.indexCount, info.instanceCount); } } From d9bf68ed06d4b96f1003d8ff85a2d191a2c3f8fa Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 16 Aug 2024 15:59:38 -0700 Subject: [PATCH 22/49] vk: fix missing read-after-write barrier (#8046) We relied on layout transition to emit a barrier, but sometimes it's skipped. In that case, we explicitly emit a barrier for the transition from attachment -> sampler. Without this, we'll hit read-after-write sync validation error and produce various artifacts (e.g a fade to white effect) on different platforms. --- filament/backend/src/vulkan/VulkanDriver.cpp | 19 +++--- filament/backend/src/vulkan/VulkanTexture.cpp | 59 +++++++++++++------ filament/backend/src/vulkan/VulkanTexture.h | 13 ++-- 3 files changed, 58 insertions(+), 33 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 3fc374c67c8..b55e365a454 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1510,21 +1510,22 @@ void VulkanDriver::endRenderPass(int) { assert_invariant(rt); // Since we might soon be sampling from the render target that we just wrote to, we need a - // pipeline barrier between framebuffer writes and shader reads. This is a memory barrier rather - // than an image barrier. If we were to use image barriers here, we would potentially need to - // issue several of them when considering MRT. This would be very complex to set up and would - // require more state tracking, so we've chosen to use a memory barrier for simplicity and - // correctness. + // pipeline barrier between framebuffer writes and shader reads. if (!rt->isSwapChain()) { for (auto& attachment: mRenderPassFboInfo.attachments) { auto const& range = attachment.getSubresourceRange(); bool const isDepth = attachment.isDepth(); + auto texture = attachment.texture; if (isDepth) { - attachment.texture->setLayout(range, VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT); - attachment.texture->transitionLayout(&commands, range, VulkanLayout::DEPTH_SAMPLER); + texture->setLayout(range, VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT); + if (!texture->transitionLayout(&commands, range, VulkanLayout::DEPTH_SAMPLER)) { + texture->attachmentToSamplerBarrier(&commands, range); + } } else { - attachment.texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT); - attachment.texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE); + texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT); + if (!texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE)) { + texture->attachmentToSamplerBarrier(&commands, range); + } } } } diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index c99b0d70737..7d35e2f3194 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -140,9 +140,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, #if FVK_ENABLED(FVK_DEBUG_TEXTURE) // Validate that the format is actually sampleable. VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(physicalDevice, mVkFormat, &props); + vkGetPhysicalDeviceFormatProperties(physicalDevice, state->mVkFormat, &props); if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) { - FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mVkFormat << " is not " + FVK_LOGW << "Texture usage is SAMPLEABLE but format " << state->mVkFormat << " is not " "sampleable with optimal tiling." << utils::io::endl; } #endif @@ -470,12 +470,12 @@ VkImageAspectFlags VulkanTexture::getImageAspect() const { return filament::backend::getImageAspect(state->mVkFormat); } -void VulkanTexture::transitionLayout(VulkanCommandBuffer* commands, +bool VulkanTexture::transitionLayout(VulkanCommandBuffer* commands, const VkImageSubresourceRange& range, VulkanLayout newLayout) { - transitionLayout(commands->buffer(), commands->fence, range, newLayout); + return transitionLayout(commands->buffer(), commands->fence, range, newLayout); } -void VulkanTexture::transitionLayout( +bool VulkanTexture::transitionLayout( VkCommandBuffer cmdbuf, std::shared_ptr fence, const VkImageSubresourceRange& range, VulkanLayout newLayout) { @@ -537,23 +537,45 @@ void VulkanTexture::transitionLayout( setLayout(range, newLayout); #if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) - FVK_LOGD << "transition texture=" << mTextureImage << " (" << range.baseArrayLayer - << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," - << range.levelCount << ")" << " from=" << oldLayout << " to=" << newLayout - << " format=" << mVkFormat << " depth=" << isVkDepthFormat(mVkFormat) - << " slice-by-slice=" << transitionSliceBySlice << utils::io::endl; + FVK_LOGD << "transition texture=" << state->mTextureImage << " (" << range.baseArrayLayer + << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," + << range.levelCount << ")" << " from=" << oldLayout << " to=" << newLayout + << " format=" << state->mVkFormat << " depth=" << isVkDepthFormat(state->mVkFormat) + << " slice-by-slice=" << transitionSliceBySlice << utils::io::endl; #endif } else { #if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION) - FVK_LOGD << "transition texture=" << mTextureImage << " (" << range.baseArrayLayer - << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," - << range.levelCount << ")" << " to=" << newLayout - << " is skipped because of no change in layout" << utils::io::endl; + FVK_LOGD << "transition texture=" << state->mTextureImage << " (" << range.baseArrayLayer + << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," + << range.levelCount << ")" << " to=" << newLayout + << " is skipped because of no change in layout" << utils::io::endl; #endif } + return hasTransitions; } -void VulkanTexture::setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout) { +void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands, + VkImageSubresourceRange const& range) { + VkCommandBuffer const cmdbuf = commands->buffer(); + auto* const state = mAllocator->handle_cast(mState); + VkImageLayout const layout + = imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel)); + VkImageMemoryBarrier barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, + .oldLayout = layout, + .newLayout = layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = state->mTextureImage, + .subresourceRange = range, + }; + vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); +} + +void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout) { auto* const state = mAllocator->handle_cast(mState); uint32_t const firstLayer = range.baseArrayLayer; uint32_t const lastLayer = firstLayer + range.layerCount; @@ -590,6 +612,7 @@ VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) void VulkanTexture::print() const { + auto* const state = mAllocator->handle_cast(mState); uint32_t const firstLayer = 0; uint32_t const lastLayer = firstLayer + mFullViewRange.layerCount; uint32_t const firstLevel = 0; @@ -602,16 +625,16 @@ void VulkanTexture::print() const { layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) && level >= mPrimaryViewRange.baseMipLevel && level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount); - FVK_LOGD << "[" << mTextureImage << "]: (" << layer << "," << level + FVK_LOGD << "[" << state->mTextureImage << "]: (" << layer << "," << level << ")=" << getLayout(layer, level) << " primary=" << primary << utils::io::endl; } } - for (auto view: mCachedImageViews) { + for (auto view: state->mCachedImageViews) { auto& range = view.first.range; - FVK_LOGD << "[" << mTextureImage << ", imageView=" << view.second << "]=>" + FVK_LOGD << "[" << state->mTextureImage << ", imageView=" << view.second << "]=>" << " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << "," << range.levelCount << ")" << " aspect=" << range.aspectMask << " viewType=" << view.first.type diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index 0082fe2e2c9..b57c9cb4c5b 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -167,11 +167,14 @@ struct VulkanTexture : public HwTexture, VulkanResource { return state->mSidecarMSAA.get(); } - void transitionLayout(VulkanCommandBuffer* commands, const VkImageSubresourceRange& range, + bool transitionLayout(VulkanCommandBuffer* commands, const VkImageSubresourceRange& range, VulkanLayout newLayout); - void transitionLayout(VkCommandBuffer cmdbuf, std::shared_ptr fence, - const VkImageSubresourceRange& range, VulkanLayout newLayout); + bool transitionLayout(VkCommandBuffer cmdbuf, std::shared_ptr fence, + VkImageSubresourceRange const& range, VulkanLayout newLayout); + + void attachmentToSamplerBarrier(VulkanCommandBuffer* commands, + VkImageSubresourceRange const& range); // Returns the preferred data plane of interest for all image views. // For now this always returns either DEPTH or COLOR. @@ -179,9 +182,7 @@ struct VulkanTexture : public HwTexture, VulkanResource { // For implicit transition like the end of a render pass, we need to be able to set the layout // manually (outside of calls to transitionLayout). - void setLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout); - - void setPrimaryLayout(const VkImageSubresourceRange& range, VulkanLayout newLayout); + void setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout); bool transitionReady() { VulkanTextureState* state = getSharedState(); From cab5fc561cdd681fd96f517d6a3e759a572f814f Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 21 Aug 2024 11:51:59 -0700 Subject: [PATCH 23/49] GLDescriptorSet: less restrictive size assert (#8070) For some platform, the compilation fails because the size of the struct is not exactly 32 bits (but is always less). --- filament/backend/src/opengl/GLDescriptorSet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/opengl/GLDescriptorSet.h b/filament/backend/src/opengl/GLDescriptorSet.h index db2548c916b..22550099ef0 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.h +++ b/filament/backend/src/opengl/GLDescriptorSet.h @@ -151,7 +151,7 @@ struct GLDescriptorSet : public HwDescriptorSet { DescriptorSetLayoutHandle dslh; // 4 uint8_t dynamicBufferCount = 0; // 1 }; -static_assert(sizeof(GLDescriptorSet) == 32); +static_assert(sizeof(GLDescriptorSet) <= 32); } // namespace filament::backend From 21d93ce49d84921ccfbd5f9a948e3c3a6d5ec593 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 21 Aug 2024 12:07:24 -0700 Subject: [PATCH 24/49] Fix missing duplication of descriptor set in material instance (#8068) --- filament/src/details/MaterialInstance.cpp | 8 +++++++- filament/src/ds/DescriptorSet.cpp | 8 ++++++++ filament/src/ds/DescriptorSet.h | 10 ++++++---- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index c635fb16529..c8726738a2c 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -113,7 +113,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name) : mMaterial(other->mMaterial), - mDescriptorSet(other->mMaterial->getDescriptorSetLayout()), + mTextureParameters(other->mTextureParameters), + mDescriptorSet(other->mDescriptorSet.duplicate(mMaterial->getDescriptorSetLayout())), mPolygonOffset(other->mPolygonOffset), mStencilState(other->mStencilState), mMaskThreshold(other->mMaskThreshold), @@ -157,6 +158,11 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, mMaterialSortingKey = RenderPass::makeMaterialSortingKey( material->getId(), material->generateMaterialInstanceId()); + + // If the original descriptor set has been commited, the copy needs to commit as well. + if (other->mDescriptorSet.getHandle()) { + mDescriptorSet.commitSlow(mMaterial->getDescriptorSetLayout(), driver); + } } FMaterialInstance* FMaterialInstance::duplicate( diff --git a/filament/src/ds/DescriptorSet.cpp b/filament/src/ds/DescriptorSet.cpp index 722224c578a..50667978408 100644 --- a/filament/src/ds/DescriptorSet.cpp +++ b/filament/src/ds/DescriptorSet.cpp @@ -131,4 +131,12 @@ void DescriptorSet::setSampler( mValid.set(binding, (bool)th); } +DescriptorSet DescriptorSet::duplicate(DescriptorSetLayout const& layout) const noexcept { + DescriptorSet set{layout}; + memcpy(set.mDescriptors.data(), mDescriptors.data(), mDescriptors.size() * sizeof(Desc)); + set.mDirty = mDirty; + set.mValid = mValid; + return set; +} + } // namespace filament diff --git a/filament/src/ds/DescriptorSet.h b/filament/src/ds/DescriptorSet.h index 6cc861efd96..4b8d6d02dfa 100644 --- a/filament/src/ds/DescriptorSet.h +++ b/filament/src/ds/DescriptorSet.h @@ -47,13 +47,14 @@ class DescriptorSet { void terminate(backend::DriverApi& driver) noexcept; // update the descriptors if needed - void commit(DescriptorSetLayout const& layout, - backend::DriverApi& driver) noexcept { + void commit(DescriptorSetLayout const& layout, backend::DriverApi& driver) noexcept { if (UTILS_UNLIKELY(mDirty.any())) { commitSlow(layout, driver); } } + void commitSlow(DescriptorSetLayout const& layout, backend::DriverApi& driver) noexcept; + // bind the descriptor set void bind(backend::DriverApi& driver, DescriptorSetBindingPoints set) const noexcept; @@ -70,6 +71,9 @@ class DescriptorSet { backend::Handle th, backend::SamplerParams params) noexcept; + // Used for duplicating material + DescriptorSet duplicate(DescriptorSetLayout const& layout) const noexcept; + backend::DescriptorSetHandle getHandle() const noexcept { return mDescriptorSetHandle; } @@ -79,8 +83,6 @@ class DescriptorSet { } private: - void commitSlow(DescriptorSetLayout const& layout, backend::DriverApi& driver) noexcept; - struct Desc { Desc() noexcept { } union { From 02bdddf50edbd254ea8a8c4a178856f989c7b62e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 21 Aug 2024 15:19:56 -0700 Subject: [PATCH 25/49] fix a too aggressive assert --- filament/src/details/Material.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index 5ef8f709aef..b7aeb5ac813 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -94,11 +94,12 @@ class FMaterial : public Material { } DescriptorSetLayout const& getPerViewDescriptorSetLayout(Variant variant) const noexcept { - assert_invariant(mMaterialDomain == MaterialDomain::SURFACE); if (Variant::isValidDepthVariant(variant)) { + assert_invariant(mMaterialDomain == MaterialDomain::SURFACE); return mEngine.getPerViewDescriptorSetLayoutDepthVariant(); } if (Variant::isSSRVariant(variant)) { + assert_invariant(mMaterialDomain == MaterialDomain::SURFACE); return mEngine.getPerViewDescriptorSetLayoutSsrVariant(); } return mPerViewDescriptorSetLayout; From bbb45218acb166cac9e3b745dc615a04ffaed2e1 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 21 Aug 2024 15:47:07 -0700 Subject: [PATCH 26/49] Fix, assertion when an external image is used but not yet set (#8073) --- filament/src/details/Texture.cpp | 69 +++++++++++++++++++++----------- filament/src/details/Texture.h | 5 +++ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 0e6b3d5c8b0..e9cd835955b 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -233,6 +233,8 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { if (mTarget == SamplerType::SAMPLER_EXTERNAL) { // mHandle and mHandleForSampling will be created in setExternalImage() + // If this Texture is used for sampling before setExternalImage() is called, + // we'll lazily create a 1x1 placeholder texture. return; } @@ -255,11 +257,7 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { // frees driver resources, object becomes invalid void FTexture::terminate(FEngine& engine) { - FEngine::DriverApi& driver = engine.getDriverApi(); - if (mHandleForSampling != mHandle) { - driver.destroyTexture(mHandleForSampling); - } - driver.destroyTexture(mHandle); + setHandles({}); } size_t FTexture::getWidth(size_t level) const noexcept { @@ -442,12 +440,7 @@ void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { // external image on this thread, if necessary. auto& api = engine.getDriverApi(); api.setupExternalImage(image); - if (mHandle) { - api.destroyTexture(mHandle); - assert_invariant(mHandleForSampling == mHandle); - } - mHandle = api.createTextureExternalImage(mFormat, mWidth, mHeight, mUsage, image); - mHandleForSampling = mHandle; + setHandles(api.createTextureExternalImage(mFormat, mWidth, mHeight, mUsage, image)); } void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noexcept { @@ -458,12 +451,7 @@ void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noex // the external image on this thread, if necessary. auto& api = engine.getDriverApi(); api.setupExternalImage(image); - if (mHandle) { - api.destroyTexture(mHandle); - assert_invariant(mHandleForSampling == mHandle); - } - mHandle = api.createTextureExternalImagePlane(mFormat, mWidth, mHeight, mUsage, image, plane); - mHandleForSampling = mHandle; + setHandles(api.createTextureExternalImagePlane(mFormat, mWidth, mHeight, mUsage, image, plane)); } void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept { @@ -528,20 +516,53 @@ void FTexture::updateLodRange(uint8_t baseLevel, uint8_t levelCount) noexcept { } } +void FTexture::setHandles(backend::Handle handle) noexcept { + assert_invariant(!mHandle || mHandleForSampling); + if (mHandle) { + mDriver->destroyTexture(mHandle); + } + if (mHandleForSampling != mHandle) { + mDriver->destroyTexture(mHandleForSampling); + } + mHandle = handle; + mHandleForSampling = handle; +} + +backend::Handle FTexture::setHandleForSampling( + backend::Handle handle) const noexcept { + assert_invariant(!mHandle || mHandleForSampling); + if (mHandleForSampling && mHandleForSampling != mHandle) { + mDriver->destroyTexture(mHandleForSampling); + } + return mHandleForSampling = handle; +} + +backend::Handle FTexture::createPlaceholderTexture( + backend::DriverApi& driver) noexcept { + auto handle = driver.createTexture( + Sampler::SAMPLER_2D, 1, InternalFormat::RGBA8, 1, 1, 1, 1, Usage::DEFAULT); + static uint8_t pixels[4] = { 0, 0, 0, 0 }; + driver.update3DImage(handle, 0, 0, 0, 0, 1, 1, 1, + { (char*)&pixels[0], sizeof(pixels), + Texture::PixelBufferDescriptor::PixelDataFormat::RGBA, + Texture::PixelBufferDescriptor::PixelDataType::UBYTE }); + return handle; +} + backend::Handle FTexture::getHwHandleForSampling() const noexcept { + if (UTILS_UNLIKELY(mTarget == SamplerType::SAMPLER_EXTERNAL && !mHandleForSampling)) { + return setHandleForSampling(createPlaceholderTexture(*mDriver)); + } + auto const& range = mLodRange; auto& activeRange = mActiveLodRange; if (UTILS_UNLIKELY(activeRange.first != range.first || activeRange.last != range.last)) { activeRange = range; - if (mHandleForSampling != mHandle) { - mDriver->destroyTexture(mHandleForSampling); - } if (range.empty()) { - mHandleForSampling = mHandle; + setHandleForSampling(mHandle); } else { - mHandleForSampling = mDriver->createTextureView(mHandle, - range.first, - range.last - range.first); + setHandleForSampling( + mDriver->createTextureView(mHandle, range.first, range.last - range.first)); } } return mHandleForSampling; diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index 96c3f380bd2..e652058c01a 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -132,6 +132,11 @@ class FTexture : public Texture { bool empty() const noexcept { return first == last; } }; void updateLodRange(uint8_t baseLevel, uint8_t levelCount) noexcept; + void setHandles(backend::Handle handle) noexcept; + backend::Handle setHandleForSampling( + backend::Handle handle) const noexcept; + static backend::Handle createPlaceholderTexture( + backend::DriverApi& driver) noexcept; backend::Handle mHandle; mutable backend::Handle mHandleForSampling; From d161ef24f5ae98b1542beb24917c7532b4244387 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 28 Aug 2024 11:52:49 -0700 Subject: [PATCH 27/49] Update createTextureView API to include swizzling, implement on Metal (#8077) --- .../include/private/backend/DriverAPI.inc | 7 +++ filament/backend/src/metal/MetalDriver.mm | 38 +++++++------ filament/backend/src/metal/MetalHandles.h | 14 +++-- filament/backend/src/metal/MetalHandles.mm | 55 ++++++++++--------- filament/backend/src/opengl/OpenGLDriver.cpp | 10 ++++ filament/backend/src/vulkan/VulkanDriver.cpp | 10 ++++ filament/src/ResourceAllocator.cpp | 28 +++++----- filament/src/details/Texture.cpp | 46 ++++++++++++---- filament/src/details/Texture.h | 4 ++ 9 files changed, 136 insertions(+), 76 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 591df4c346f..a7d29e92808 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -205,6 +205,13 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureView, uint8_t, baseLevel, uint8_t, levelCount) +DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureViewSwizzle, + backend::TextureHandle, texture, + backend::TextureSwizzle, r, + backend::TextureSwizzle, g, + backend::TextureSwizzle, b, + backend::TextureSwizzle, a) + DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureSwizzled, backend::SamplerType, target, uint8_t, levels, diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index fd6b45e30a4..3829566eeb3 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -448,10 +448,8 @@ auto& sc = mContext->sampleCountLookup; samples = sc[std::min(MAX_SAMPLE_COUNT, samples)]; - mContext->textures.insert(construct_handle(th, *mContext, - target, levels, format, samples, width, height, depth, usage, - TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, - TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3)); + mContext->textures.insert(construct_handle( + th, *mContext, target, levels, format, samples, width, height, depth, usage)); DEBUG_LOG( "createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = " @@ -463,24 +461,24 @@ TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { - // Clamp sample count to what the device supports. - auto& sc = mContext->sampleCountLookup; - samples = sc[std::min(MAX_SAMPLE_COUNT, samples)]; - - mContext->textures.insert(construct_handle(th, *mContext, - target, levels, format, samples, width, height, depth, usage, r, g, b, a)); + auto texture = alloc_handle(); + createTextureR(texture, target, levels, format, samples, width, height, depth, usage); + createTextureViewSwizzleR(th, texture, r, g, b, a); + destroyTexture(texture); +} - DEBUG_LOG( - "createTextureSwizzledR(th = %d, target = %s, levels = %d, format = ?, samples = %d, " - "width = %d, height = %d, depth = %d, usage = %s, r = ?, g = ?, b = ?, a = ?)\n", - th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); +void MetalDriver::createTextureViewR( + Handle th, Handle srch, uint8_t baseLevel, uint8_t levelCount) { + MetalTexture const* src = handle_cast(srch); + mContext->textures.insert( + construct_handle(th, *mContext, src, baseLevel, levelCount)); } -void MetalDriver::createTextureViewR(Handle th, - Handle srch, uint8_t baseLevel, uint8_t levelCount) { +void MetalDriver::createTextureViewSwizzleR(Handle th, Handle srch, + backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, + backend::TextureSwizzle a) { MetalTexture const* src = handle_cast(srch); - mContext->textures.insert(construct_handle(th, *mContext, - src, baseLevel, levelCount)); + mContext->textures.insert(construct_handle(th, *mContext, src, r, g, b, a)); } void MetalDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, @@ -711,6 +709,10 @@ return alloc_handle(); } +Handle MetalDriver::createTextureViewSwizzleS() noexcept { + return alloc_handle(); +} + Handle MetalDriver::createTextureExternalImageS() noexcept { return alloc_handle(); } diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 2c108ecd82a..398b26ff617 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -229,12 +229,14 @@ struct PixelBufferShape { class MetalTexture : public HwTexture { public: MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, - uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, - TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) noexcept; - - // constructor for creating a texture view - MetalTexture(MetalContext& context, - MetalTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept; + uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, + TextureUsage usage) noexcept; + + // constructors for creating texture views + MetalTexture(MetalContext& context, MetalTexture const* src, uint8_t baseLevel, + uint8_t levelCount) noexcept; + MetalTexture(MetalContext& context, MetalTexture const* src, TextureSwizzle r, TextureSwizzle g, + TextureSwizzle b, TextureSwizzle a) noexcept; // Constructor for importing an id outside of Filament. MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index dbfcff07b95..5a23e460846 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -494,9 +494,9 @@ static void func(void* user) { MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, - TextureUsage usage, TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, - TextureSwizzle a) noexcept + TextureUsage usage) noexcept : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context) { + assert_invariant(target != SamplerType::SAMPLER_EXTERNAL); devicePixelFormat = decidePixelFormat(&context, format); FILAMENT_CHECK_POSTCONDITION(devicePixelFormat != MTLPixelFormatInvalid) @@ -582,16 +582,28 @@ static void func(void* user) { << ", levels = " << int(levels) << ", MTLPixelFormat = " << int(devicePixelFormat) << ", width = " << width << ", height = " << height << ", depth = " << depth << "). Out of memory?"; +} - // If swizzling is set, set up a swizzled texture view that we'll use when sampling this texture. - const bool isDefaultSwizzle = - r == TextureSwizzle::CHANNEL_0 && - g == TextureSwizzle::CHANNEL_1 && - b == TextureSwizzle::CHANNEL_2 && - a == TextureSwizzle::CHANNEL_3; - // If texture is nil, then it must be a SAMPLER_EXTERNAL texture. - // Swizzling for external textures is handled inside MetalExternalImage. - if (!isDefaultSwizzle && texture && context.supportsTextureSwizzling) { +MetalTexture::MetalTexture(MetalContext& context, MetalTexture const* src, uint8_t baseLevel, + uint8_t levelCount) noexcept + : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, + src->format, src->usage), + context(context), + devicePixelFormat(src->devicePixelFormat), + externalImage(src->externalImage) { + texture = createTextureViewWithLodRange( + src->getMtlTextureForRead(), baseLevel, baseLevel + levelCount - 1); +} + +MetalTexture::MetalTexture(MetalContext& context, MetalTexture const* src, TextureSwizzle r, + TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) noexcept + : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, + src->format, src->usage), + context(context), + devicePixelFormat(src->devicePixelFormat), + externalImage(src->externalImage) { + texture = src->getMtlTextureForRead(); + if (context.supportsTextureSwizzling) { // Even though we've already checked context.supportsTextureSwizzling, we still need to // guard these calls with @availability, otherwise the API usage will generate compiler // warnings. @@ -602,15 +614,6 @@ static void func(void* user) { } } -MetalTexture::MetalTexture(MetalContext& context, - MetalTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept - : HwTexture(src->target, src->levels, src->samples, - src->width, src->height, src->depth, src->format, src->usage), - context(context) { - texture = createTextureViewWithLodRange( - src->getMtlTextureForRead(), baseLevel, baseLevel + levelCount - 1); -} - MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, id metalTexture) noexcept @@ -623,14 +626,18 @@ static void func(void* user) { : HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage), context(context), externalImage(std::make_shared( - MetalExternalImage::createFromImage(context, image))) {} + MetalExternalImage::createFromImage(context, image))) { + texture = externalImage->getMtlTexture(); +} MetalTexture::MetalTexture(MetalContext& context, TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, CVPixelBufferRef image, uint32_t plane) noexcept : HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage), context(context), externalImage(std::make_shared( - MetalExternalImage::createFromImagePlane(context, image, plane))) {} + MetalExternalImage::createFromImagePlane(context, image, plane))) { + texture = externalImage->getMtlTexture(); +} void MetalTexture::terminate() noexcept { texture = nil; @@ -641,10 +648,6 @@ static void func(void* user) { } id MetalTexture::getMtlTextureForRead() const noexcept { - if (target == SamplerType::SAMPLER_EXTERNAL) { - return externalImage->getMtlTexture(); - } - // TODO: get LODs to work correctly with swizzling return swizzledTextureView ? swizzledTextureView : texture; } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 43d774b2011..46110f724e8 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -552,6 +552,10 @@ Handle OpenGLDriver::createTextureViewS() noexcept { return initHandle(); } +Handle OpenGLDriver::createTextureViewSwizzleS() noexcept { + return initHandle(); +} + Handle OpenGLDriver::createTextureExternalImageS() noexcept { return initHandle(); } @@ -995,6 +999,12 @@ void OpenGLDriver::createTextureViewR(Handle th, CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::createTextureViewSwizzleR(Handle th, Handle srch, + backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, + backend::TextureSwizzle a) { + // TODO: implement swizzle +} + void OpenGLDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { createTextureR(th, SamplerType::SAMPLER_EXTERNAL, 1, format, 1, width, height, 1, usage); diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 673af25691b..c002ca98787 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -581,6 +581,12 @@ void VulkanDriver::createTextureViewR(Handle th, mResourceManager.acquire(vktexture); } +void VulkanDriver::createTextureViewSwizzleR(Handle th, Handle srch, + backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, + backend::TextureSwizzle a) { + // TODO: implement swizzle +} + void VulkanDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { } @@ -782,6 +788,10 @@ Handle VulkanDriver::createTextureViewS() noexcept { return mResourceAllocator.allocHandle(); } +Handle VulkanDriver::createTextureViewSwizzleS() noexcept { + return mResourceAllocator.allocHandle(); +} + Handle VulkanDriver::createTextureExternalImageS() noexcept { return mResourceAllocator.allocHandle(); } diff --git a/filament/src/ResourceAllocator.cpp b/filament/src/ResourceAllocator.cpp index 471f8778bd8..f935c2d2fe8 100644 --- a/filament/src/ResourceAllocator.cpp +++ b/filament/src/ResourceAllocator.cpp @@ -181,24 +181,24 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name, textureCache.erase(it); } else { // we don't, allocate a new texture and populate the in-use list - if (swizzle == defaultSwizzle) { - handle = mBackend.createTexture( - target, levels, format, samples, width, height, depth, usage); - } else { - handle = mBackend.createTextureSwizzled( - target, levels, format, samples, width, height, depth, usage, - swizzle[0], swizzle[1], swizzle[2], swizzle[3]); + handle = mBackend.createTexture( + target, levels, format, samples, width, height, depth, usage); + if (swizzle != defaultSwizzle) { + TextureHandle swizzledHandle = mBackend.createTextureViewSwizzle( + handle, swizzle[0], swizzle[1], swizzle[2], swizzle[3]); + mBackend.destroyTexture(handle); + handle = swizzledHandle; } } mDisposer->checkout(handle, key); } else { - if (swizzle == defaultSwizzle) { - handle = mBackend.createTexture( - target, levels, format, samples, width, height, depth, usage); - } else { - handle = mBackend.createTextureSwizzled( - target, levels, format, samples, width, height, depth, usage, - swizzle[0], swizzle[1], swizzle[2], swizzle[3]); + handle = mBackend.createTexture( + target, levels, format, samples, width, height, depth, usage); + if (swizzle != defaultSwizzle) { + TextureHandle swizzledHandle = mBackend.createTextureViewSwizzle( + handle, swizzle[0], swizzle[1], swizzle[2], swizzle[3]); + mBackend.destroyTexture(handle); + handle = swizzledHandle; } } return handle; diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index c1e574c2602..c881b488207 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -230,6 +230,8 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { mUsage = builder->mUsage; mTarget = builder->mTarget; mLevelCount = builder->mLevels; + mSwizzle = builder->mSwizzle; + mTextureIsSwizzled = builder->mTextureIsSwizzled; if (mTarget == SamplerType::SAMPLER_EXTERNAL) { // mHandle and mHandleForSampling will be created in setExternalImage() @@ -239,20 +241,20 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { } if (UTILS_LIKELY(builder->mImportedId == 0)) { - if (UTILS_LIKELY(!builder->mTextureIsSwizzled)) { - mHandle = driver.createTexture( - mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage); - } else { - mHandle = driver.createTextureSwizzled( - mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage, - builder->mSwizzle[0], builder->mSwizzle[1], builder->mSwizzle[2], - builder->mSwizzle[3]); - } + mHandle = driver.createTexture( + mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage); } else { mHandle = driver.importTexture(builder->mImportedId, mTarget, mLevelCount, mFormat, mSampleCount, mWidth, mHeight, mDepth, mUsage); } + if (UTILS_UNLIKELY(builder->mTextureIsSwizzled)) { + auto const& s = builder->mSwizzle; + auto swizzleView = driver.createTextureViewSwizzle(mHandle, s[0], s[1], s[2], s[3]); + driver.destroyTexture(mHandle); + mHandle = swizzleView; + } + mHandleForSampling = mHandle; if (auto name = builder.getName(); !name.empty()) { @@ -445,7 +447,17 @@ void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { // external image on this thread, if necessary. auto& api = engine.getDriverApi(); api.setupExternalImage(image); - setHandles(api.createTextureExternalImage(mFormat, mWidth, mHeight, mUsage, image)); + + auto texture = api.createTextureExternalImage(mFormat, mWidth, mHeight, mUsage, image); + + if (mTextureIsSwizzled) { + auto const& s = mSwizzle; + auto swizzleView = api.createTextureViewSwizzle(texture, s[0], s[1], s[2], s[3]); + api.destroyTexture(texture); + texture = swizzleView; + } + + setHandles(texture); } void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noexcept { @@ -456,7 +468,18 @@ void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noex // the external image on this thread, if necessary. auto& api = engine.getDriverApi(); api.setupExternalImage(image); - setHandles(api.createTextureExternalImagePlane(mFormat, mWidth, mHeight, mUsage, image, plane)); + + auto texture = + api.createTextureExternalImagePlane(mFormat, mWidth, mHeight, mUsage, image, plane); + + if (mTextureIsSwizzled) { + auto const& s = mSwizzle; + auto swizzleView = api.createTextureViewSwizzle(texture, s[0], s[1], s[2], s[3]); + api.destroyTexture(texture); + texture = swizzleView; + } + + setHandles(texture); } void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept { @@ -493,7 +516,6 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept { } bool FTexture::textureHandleCanMutate() const noexcept { - // TODO: this will eventually include swizzling return (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) || mTarget == SamplerType::SAMPLER_EXTERNAL; } diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index e652058c01a..1aadfd6450a 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -151,6 +151,10 @@ class FTexture : public Texture { Sampler mTarget = Sampler::SAMPLER_2D; uint8_t mLevelCount = 1; uint8_t mSampleCount = 1; + std::array mSwizzle = { + Swizzle::CHANNEL_0, Swizzle::CHANNEL_1, + Swizzle::CHANNEL_2, Swizzle::CHANNEL_3 }; + bool mTextureIsSwizzled; Usage mUsage = Usage::DEFAULT; // there is 7 bytes of padding here From 6de6a2123d61d9fb625abaac7a82dd5854c731f4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 28 Aug 2024 12:53:50 -0700 Subject: [PATCH 28/49] implement createTextureViewSwizzled() in the GL backend --- .../include/private/backend/DriverAPI.inc | 14 --- filament/backend/src/metal/MetalDriver.mm | 14 --- .../backend/src/opengl/GLDescriptorSet.cpp | 31 ++++--- filament/backend/src/opengl/GLDescriptorSet.h | 15 +++- filament/backend/src/opengl/GLTexture.h | 18 ++++ filament/backend/src/opengl/OpenGLDriver.cpp | 90 ++++++++++++------- filament/backend/src/vulkan/VulkanDriver.cpp | 28 +++--- 7 files changed, 124 insertions(+), 86 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index a7d29e92808..89b9018c8cf 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -212,20 +212,6 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureViewSwizzle, backend::TextureSwizzle, b, backend::TextureSwizzle, a) -DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureSwizzled, - backend::SamplerType, target, - uint8_t, levels, - backend::TextureFormat, format, - uint8_t, samples, - uint32_t, width, - uint32_t, height, - uint32_t, depth, - backend::TextureUsage, usage, - backend::TextureSwizzle, r, - backend::TextureSwizzle, g, - backend::TextureSwizzle, b, - backend::TextureSwizzle, a) - DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage, backend::TextureFormat, format, uint32_t, width, diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 3829566eeb3..d137b2319b5 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -457,16 +457,6 @@ th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage)); } -void MetalDriver::createTextureSwizzledR(Handle th, SamplerType target, uint8_t levels, - TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, TextureUsage usage, - TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { - auto texture = alloc_handle(); - createTextureR(texture, target, levels, format, samples, width, height, depth, usage); - createTextureViewSwizzleR(th, texture, r, g, b, a); - destroyTexture(texture); -} - void MetalDriver::createTextureViewR( Handle th, Handle srch, uint8_t baseLevel, uint8_t levelCount) { MetalTexture const* src = handle_cast(srch); @@ -701,10 +691,6 @@ return alloc_handle(); } -Handle MetalDriver::createTextureSwizzledS() noexcept { - return alloc_handle(); -} - Handle MetalDriver::createTextureViewS() noexcept { return alloc_handle(); } diff --git a/filament/backend/src/opengl/GLDescriptorSet.cpp b/filament/backend/src/opengl/GLDescriptorSet.cpp index 0667f8497ed..65eb275d810 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.cpp +++ b/filament/backend/src/opengl/GLDescriptorSet.cpp @@ -37,9 +37,10 @@ #include #include #include - #include + #include +#include #include #include @@ -50,7 +51,7 @@ namespace filament::backend { GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh, GLDescriptorSetLayout const* layout) noexcept : descriptors(layout->maxDescriptorBinding + 1), - dslh(dslh) { + dslh(std::move(dslh)) { // We have allocated enough storage for all descriptors. Now allocate the empty descriptor // themselves. @@ -178,6 +179,7 @@ void GLDescriptorSet::update(OpenGLContext& gl, arg.ref = t->ref; arg.baseLevel = t->gl.baseLevel; arg.maxLevel = t->gl.maxLevel; + arg.swizzle = t->gl.swizzle; } #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 arg.sampler = gl.getSampler(params); @@ -195,17 +197,17 @@ void GLDescriptorSet::update(OpenGLContext& gl, } template -void GLDescriptorSet::updateTextureLod(OpenGLContext& gl, +void GLDescriptorSet::updateTextureView(OpenGLContext& gl, HandleAllocatorGL& handleAllocator, GLuint unit, T const& desc) noexcept { // The common case is that we don't have a ref handle (we only have one if - // the texture ever had a View on it. + // the texture ever had a View on it). + assert_invariant(desc.ref); GLTextureRef* const ref = handleAllocator.handle_cast(desc.ref); - if (UTILS_UNLIKELY((desc.baseLevel != ref->baseLevel - || desc.maxLevel != ref->maxLevel))) { + if (UTILS_UNLIKELY((desc.baseLevel != ref->baseLevel || desc.maxLevel != ref->maxLevel))) { // If we have views, then it's still uncommon that we'll switch often // handle the case where we reset to the original texture - GLint baseLevel = desc.baseLevel; - GLint maxLevel = desc.maxLevel; + GLint baseLevel = GLint(desc.baseLevel); // NOLINT(*-signed-char-misuse) + GLint maxLevel = GLint(desc.maxLevel); // NOLINT(*-signed-char-misuse) if (baseLevel > maxLevel) { baseLevel = 0; maxLevel = 1000; // per OpenGL spec @@ -217,6 +219,15 @@ void GLDescriptorSet::updateTextureLod(OpenGLContext& gl, ref->baseLevel = desc.baseLevel; ref->maxLevel = desc.maxLevel; } + if (UTILS_UNLIKELY(desc.swizzle != ref->swizzle)) { + using namespace GLUtils; + gl.activeTexture(unit); + glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_R, (GLint)getSwizzleChannel(desc.swizzle[0])); + glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_G, (GLint)getSwizzleChannel(desc.swizzle[1])); + glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_B, (GLint)getSwizzleChannel(desc.swizzle[2])); + glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_A, (GLint)getSwizzleChannel(desc.swizzle[3])); + ref->swizzle = desc.swizzle; + } } void GLDescriptorSet::bind( @@ -272,7 +283,7 @@ void GLDescriptorSet::bind( gl.bindTexture(unit, arg.target, arg.id); gl.bindSampler(unit, arg.sampler); if (UTILS_UNLIKELY(arg.ref)) { - updateTextureLod(gl, handleAllocator, unit, arg); + updateTextureView(gl, handleAllocator, unit, arg); } } else { gl.unbindTextureUnit(unit); @@ -283,7 +294,7 @@ void GLDescriptorSet::bind( gl.bindTexture(unit, arg.target, arg.id); gl.bindSampler(unit, arg.sampler); if (UTILS_UNLIKELY(arg.ref)) { - updateTextureLod(gl, handleAllocator, unit, arg); + updateTextureView(gl, handleAllocator, unit, arg); } #if defined(GL_EXT_texture_filter_anisotropic) // Driver claims to support anisotropic filtering, but it fails when set on diff --git a/filament/backend/src/opengl/GLDescriptorSet.h b/filament/backend/src/opengl/GLDescriptorSet.h index 22550099ef0..26a783b740c 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.h +++ b/filament/backend/src/opengl/GLDescriptorSet.h @@ -31,6 +31,7 @@ #include +#include #include #include @@ -112,6 +113,12 @@ struct GLDescriptorSet : public HwDescriptorSet { Handle ref; // 4 int8_t baseLevel = 0x7f; // 1 int8_t maxLevel = -1; // 1 + std::array swizzle{ // 4 + TextureSwizzle::CHANNEL_0, + TextureSwizzle::CHANNEL_1, + TextureSwizzle::CHANNEL_2, + TextureSwizzle::CHANNEL_3 + }; }; struct SamplerWithAnisotropyWorkaround { @@ -122,6 +129,12 @@ struct GLDescriptorSet : public HwDescriptorSet { math::half anisotropy = 1.0f; // 2 int8_t baseLevel = 0x7f; // 1 int8_t maxLevel = -1; // 1 + std::array swizzle{ // 4 + TextureSwizzle::CHANNEL_0, + TextureSwizzle::CHANNEL_1, + TextureSwizzle::CHANNEL_2, + TextureSwizzle::CHANNEL_3 + }; }; // A sampler descriptor for ES2 @@ -143,7 +156,7 @@ struct GLDescriptorSet : public HwDescriptorSet { static_assert(sizeof(Descriptor) <= 32); template - static void updateTextureLod(OpenGLContext& gl, + static void updateTextureView(OpenGLContext& gl, HandleAllocatorGL& handleAllocator, GLuint unit, T const& desc) noexcept; utils::FixedCapacityVector descriptors; // 16 diff --git a/filament/backend/src/opengl/GLTexture.h b/filament/backend/src/opengl/GLTexture.h index 63a18a4d270..5d721d3c214 100644 --- a/filament/backend/src/opengl/GLTexture.h +++ b/filament/backend/src/opengl/GLTexture.h @@ -22,17 +22,29 @@ #include "gl_headers.h" #include +#include #include +#include + #include namespace filament::backend { struct GLTextureRef { GLTextureRef() = default; + // view reference counter uint16_t count = 1; + // current per-view values of the texture (in GL we can only have a single View active at + // a time, and this tracks that state). It's used to avoid unnecessarily change state. int8_t baseLevel = 127; int8_t maxLevel = -1; + std::array swizzle{ + TextureSwizzle::CHANNEL_0, + TextureSwizzle::CHANNEL_1, + TextureSwizzle::CHANNEL_2, + TextureSwizzle::CHANNEL_3 + }; }; struct GLTexture : public HwTexture { @@ -52,6 +64,12 @@ struct GLTexture : public HwTexture { bool imported : 1; uint8_t sidecarSamples : 4; uint8_t reserved1 : 3; + std::array swizzle{ + TextureSwizzle::CHANNEL_0, + TextureSwizzle::CHANNEL_1, + TextureSwizzle::CHANNEL_2, + TextureSwizzle::CHANNEL_3 + }; } gl; mutable Handle ref; OpenGLPlatform::ExternalTexture* externalTexture = nullptr; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 46110f724e8..3e76e9d51dd 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -544,10 +544,6 @@ Handle OpenGLDriver::createTextureS() noexcept { return initHandle(); } -Handle OpenGLDriver::createTextureSwizzledS() noexcept { - return initHandle(); -} - Handle OpenGLDriver::createTextureViewS() noexcept { return initHandle(); } @@ -927,32 +923,6 @@ void OpenGLDriver::createTextureR(Handle th, SamplerType target, uint CHECK_GL_ERROR(utils::slog.e) } -void OpenGLDriver::createTextureSwizzledR(Handle th, - SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, - uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage, - TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { - DEBUG_MARKER() - - assert_invariant(uint8_t(usage) & uint8_t(TextureUsage::SAMPLEABLE)); - - createTextureR(th, target, levels, format, samples, w, h, depth, usage); - - // WebGL does not support swizzling. We assert for this in the Texture builder, - // so it is probably fine to silently ignore the swizzle state here. -#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2) - if (!mContext.isES2()) { - // the texture is still bound and active from createTextureR - GLTexture* t = handle_cast(th); - glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_R, (GLint)getSwizzleChannel(r)); - glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_G, (GLint)getSwizzleChannel(g)); - glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_B, (GLint)getSwizzleChannel(b)); - glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_A, (GLint)getSwizzleChannel(a)); - } -#endif - - CHECK_GL_ERROR(utils::slog.e) -} - void OpenGLDriver::createTextureViewR(Handle th, Handle srch, uint8_t baseLevel, uint8_t levelCount) { DEBUG_MARKER() @@ -1002,7 +972,65 @@ void OpenGLDriver::createTextureViewR(Handle th, void OpenGLDriver::createTextureViewSwizzleR(Handle th, Handle srch, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, backend::TextureSwizzle a) { - // TODO: implement swizzle + + DEBUG_MARKER() + GLTexture const* const src = handle_cast(srch); + + FILAMENT_CHECK_PRECONDITION(any(src->usage & TextureUsage::SAMPLEABLE)) + << "TextureView can only be created on a SAMPLEABLE texture"; + + FILAMENT_CHECK_PRECONDITION(!src->gl.imported) + << "TextureView can't be created on imported textures"; + + if (!src->ref) { + // lazily create the ref handle, because most textures will never get a texture view + src->ref = initHandle(); + } + + GLTexture* t = construct(th, + src->target, + src->levels, + src->samples, + src->width, src->height, src->depth, + src->format, + src->usage); + + t->gl = src->gl; + t->gl.baseLevel = src->gl.baseLevel; + t->gl.maxLevel = src->gl.maxLevel; + t->gl.sidecarRenderBufferMS = 0; + t->gl.sidecarSamples = 1; + + auto getChannel = [&swizzle = src->gl.swizzle](TextureSwizzle ch) { + switch (ch) { + case TextureSwizzle::SUBSTITUTE_ZERO: + case TextureSwizzle::SUBSTITUTE_ONE: + return ch; + case TextureSwizzle::CHANNEL_0: + return swizzle[0]; + case TextureSwizzle::CHANNEL_1: + return swizzle[1]; + case TextureSwizzle::CHANNEL_2: + return swizzle[2]; + case TextureSwizzle::CHANNEL_3: + return swizzle[3]; + } + }; + + t->gl.swizzle = { + getChannel(r), + getChannel(g), + getChannel(b), + getChannel(a), + }; + + // increase reference count to this texture handle + t->ref = src->ref; + GLTextureRef* ref = handle_cast(t->ref); + assert_invariant(ref); + ref->count++; + + CHECK_GL_ERROR(utils::slog.e) } void OpenGLDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index c002ca98787..ec219092279 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -559,18 +559,18 @@ void VulkanDriver::createTextureR(Handle th, SamplerType target, uint mResourceManager.acquire(vktexture); } -void VulkanDriver::createTextureSwizzledR(Handle th, SamplerType target, uint8_t levels, - TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage usage, - TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { - TextureSwizzle swizzleArray[] = {r, g, b, a}; - const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray); - auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), - mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, - target, levels, format, samples, w, h, depth, usage, mStagePool, - false /*heap allocated */, swizzleMap); - mResourceManager.acquire(vktexture); -} +//void VulkanDriver::createTextureSwizzledR(Handle th, SamplerType target, uint8_t levels, +// TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, +// TextureUsage usage, +// TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { +// TextureSwizzle swizzleArray[] = {r, g, b, a}; +// const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray); +// auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), +// mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, +// target, levels, format, samples, w, h, depth, usage, mStagePool, +// false /*heap allocated */, swizzleMap); +// mResourceManager.acquire(vktexture); +//} void VulkanDriver::createTextureViewR(Handle th, Handle srch, uint8_t baseLevel, uint8_t levelCount) { @@ -780,10 +780,6 @@ Handle VulkanDriver::createTextureS() noexcept { return mResourceAllocator.allocHandle(); } -Handle VulkanDriver::createTextureSwizzledS() noexcept { - return mResourceAllocator.allocHandle(); -} - Handle VulkanDriver::createTextureViewS() noexcept { return mResourceAllocator.allocHandle(); } From e95f0ed6a0b6519722d0f63bee66a4507a78e64c Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 29 Aug 2024 10:00:51 -0700 Subject: [PATCH 29/49] vk: implement createTextureViewSwizzled() --- filament/backend/src/vulkan/VulkanDriver.cpp | 19 ++- filament/backend/src/vulkan/VulkanTexture.cpp | 131 +++++++++--------- filament/backend/src/vulkan/VulkanTexture.h | 31 +++-- filament/backend/src/vulkan/VulkanUtility.cpp | 2 +- filament/backend/src/vulkan/VulkanUtility.h | 2 +- 5 files changed, 98 insertions(+), 87 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index aaad45984f4..fe552d71a02 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -572,19 +572,26 @@ void VulkanDriver::createTextureR(Handle th, SamplerType target, uint // mResourceManager.acquire(vktexture); //} -void VulkanDriver::createTextureViewR(Handle th, - Handle srch, uint8_t baseLevel, uint8_t levelCount) { +void VulkanDriver::createTextureViewR(Handle th, Handle srch, + uint8_t baseLevel, uint8_t levelCount) { VulkanTexture const* src = mResourceAllocator.handle_cast(srch); - auto vktexture = mResourceAllocator.construct(th, - mPlatform->getDevice(), mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, - &mResourceAllocator, src, baseLevel, levelCount, mStagePool); + auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, + src, baseLevel, levelCount); mResourceManager.acquire(vktexture); } void VulkanDriver::createTextureViewSwizzleR(Handle th, Handle srch, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, backend::TextureSwizzle a) { - // TODO: implement swizzle + TextureSwizzle const swizzleArray[] = {r, g, b, a}; + VkComponentMapping const swizzle = getSwizzleMap(swizzleArray); + + VulkanTexture const* src = mResourceAllocator.handle_cast(srch); + auto vktexture = mResourceAllocator.construct(th, mPlatform->getDevice(), + mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator, + src, swizzle); + mResourceManager.acquire(vktexture); } void VulkanDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 1a725abd5be..e40258884bf 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -29,15 +29,35 @@ using namespace bluevk; namespace filament::backend { +namespace { + +inline uint8_t getLayerCount(SamplerType const target, uint32_t const depth) { + switch (target) { + case SamplerType::SAMPLER_2D: + case SamplerType::SAMPLER_3D: + case SamplerType::SAMPLER_EXTERNAL: + return 1; + case SamplerType::SAMPLER_CUBEMAP: + return 6; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: + return depth * 6; + case SamplerType::SAMPLER_2D_ARRAY: + return depth; + } +} + +} // anonymous namespace VulkanTextureState::VulkanTextureState( VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, - VkFormat format, VkImageViewType viewType, VkComponentMapping swizzle) + VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount) : VulkanResource(VulkanResourceType::HEAP_ALLOCATED), mVkFormat(format), mViewType(viewType), - mSwizzle(swizzle), + mFullViewRange { + filament::backend::getImageAspect(format), 0, levels, 0, layerCount + }, mStagePool(stagePool), mDevice(device), mAllocator(allocator), @@ -59,43 +79,32 @@ VulkanTexture::VulkanTexture( VulkanResourceAllocator* handleAllocator, VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated) - : HwTexture( - SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, tusage), + : HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED, + tusage), VulkanResource( heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), mAllocator(handleAllocator), mState(handleAllocator->initHandle( device, allocator, commands, stagePool, - format, imgutil::getViewType(SamplerType::SAMPLER_2D), VkComponentMapping{})), - - mFullViewRange{ - .aspectMask = getImageAspect(), - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - mPrimaryViewRange(mFullViewRange) -{ - auto* const state = handleAllocator->handle_cast(mState); + format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1)) { + auto* const state = getSharedState(); state->mTextureImage = image; + mPrimaryViewRange = state->mFullViewRange; } VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, - VulkanResourceAllocator* handleAllocator, - SamplerType target, uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, - uint32_t h, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool, - bool heapAllocated, VkComponentMapping swizzle) + VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels, + TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, + TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated) : HwTexture(target, levels, samples, w, h, depth, tformat, tusage), VulkanResource( heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE), mAllocator(handleAllocator), - mState(handleAllocator->initHandle( - device, allocator, commands, stagePool, - backend::getVkFormat(tformat), imgutil::getViewType(target), swizzle)) -{ - auto* const state = handleAllocator->handle_cast(mState); + mState(handleAllocator->initHandle(device, allocator, commands, stagePool, + backend::getVkFormat(tformat), imgutil::getViewType(target), levels, + getLayerCount(target, depth))) { + auto* const state = getSharedState(); // Create an appropriately-sized device-only VkImage, but do not fill it yet. VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -225,59 +234,52 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, error = vkBindImageMemory(state->mDevice, state->mTextureImage, state->mTextureImageMemory, 0); FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image."; - uint32_t layerCount = 0; - if (target == SamplerType::SAMPLER_CUBEMAP) { - layerCount = 6; - } else if (target == SamplerType::SAMPLER_CUBEMAP_ARRAY) { - layerCount = depth * 6; - } else if (target == SamplerType::SAMPLER_2D_ARRAY) { - layerCount = depth; - } else if (target == SamplerType::SAMPLER_3D) { - layerCount = 1; - } else { - layerCount = 1; - } - - mFullViewRange = { - .aspectMask = getImageAspect(), - .baseMipLevel = 0, - .levelCount = levels, - .baseArrayLayer = 0, - .layerCount = layerCount, - }; - // Spec out the "primary" VkImageView that shaders use to sample from the image. - mPrimaryViewRange = mFullViewRange; + mPrimaryViewRange = state->mFullViewRange; // Go ahead and create the primary image view. - getImageView(mPrimaryViewRange, state->mViewType, state->mSwizzle); + getImageView(mPrimaryViewRange, state->mViewType, mSwizzle); VulkanCommandBuffer& commandsBuf = state->mCommands->get(); commandsBuf.acquire(this); - transitionLayout(&commandsBuf, mFullViewRange, imgutil::getDefaultLayout(imageInfo.usage)); + transitionLayout(&commandsBuf, mPrimaryViewRange, imgutil::getDefaultLayout(imageInfo.usage)); } VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, - VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount, - VulkanStagePool& stagePool) + VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount) : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, src->format, src->usage), VulkanResource(VulkanResourceType::TEXTURE), mAllocator(handleAllocator) { mState = src->mState; - auto* state = handleAllocator->handle_cast(mState); + auto* state = getSharedState(); + state->refs++; - mFullViewRange = src->mFullViewRange; mPrimaryViewRange = src->mPrimaryViewRange; mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel; mPrimaryViewRange.levelCount = levelCount; } +VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, + VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + VulkanTexture const* src, VkComponentMapping swizzle) + : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, + src->format, src->usage), + VulkanResource(VulkanResourceType::TEXTURE), + mAllocator(handleAllocator) { + mState = src->mState; + auto* state = getSharedState(); + state->refs++; + mPrimaryViewRange = src->mPrimaryViewRange; + mSwizzle = swizzle; +} + VulkanTexture::~VulkanTexture() { - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); state->refs--; if (state->refs == 0) { if (state->mTextureImageMemory != VK_NULL_HANDLE) { @@ -296,7 +298,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt assert_invariant(width <= this->width && height <= this->height); assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP || target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1)); - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; @@ -383,7 +385,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height, uint32_t depth, uint32_t miplevel) { - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); void* mapped = nullptr; VulkanStageImage const* stage = state->mStagePool.acquireImage(hostData.format, hostData.type, width, height); @@ -433,13 +435,12 @@ VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange ra } VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) { - auto* const state = mAllocator->handle_cast(mState); - return getImageView(range, type, state->mSwizzle); + return getImageView(range, type, mSwizzle); } VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) { - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle }; auto iter = state->mCachedImageViews.find(key); if (iter != state->mCachedImageViews.end()) { @@ -463,7 +464,7 @@ VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageVi VkImageAspectFlags VulkanTexture::getImageAspect() const { // Helper function in VulkanUtility - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); return filament::backend::getImageAspect(state->mVkFormat); } @@ -476,7 +477,7 @@ bool VulkanTexture::transitionLayout( VkCommandBuffer cmdbuf, std::shared_ptr fence, const VkImageSubresourceRange& range, VulkanLayout newLayout) { - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel); uint32_t const firstLayer = range.baseArrayLayer; @@ -554,7 +555,7 @@ bool VulkanTexture::transitionLayout( void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range) { VkCommandBuffer const cmdbuf = commands->buffer(); - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); VkImageLayout const layout = imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel)); VkImageMemoryBarrier barrier = { @@ -573,7 +574,7 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands, } void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout) { - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); uint32_t const firstLayer = range.baseArrayLayer; uint32_t const lastLayer = firstLayer + range.layerCount; uint32_t const firstLevel = range.baseMipLevel; @@ -599,7 +600,7 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { assert_invariant(level <= 0xffff && layer <= 0xffff); - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); const uint32_t key = (layer << 16) | level; if (!state->mSubresourceLayouts.has(key)) { return VulkanLayout::UNDEFINED; @@ -609,7 +610,7 @@ VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { #if FVK_ENABLED(FVK_DEBUG_TEXTURE) void VulkanTexture::print() const { - auto* const state = mAllocator->handle_cast(mState); + auto* const state = getSharedState(); uint32_t const firstLayer = 0; uint32_t const lastLayer = firstLayer + mFullViewRange.layerCount; uint32_t const firstLevel = 0; diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index b57c9cb4c5b..69c150fa662 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -35,7 +35,7 @@ class VulkanResourceAllocator; struct VulkanTextureState : public VulkanResource { VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, - VkFormat format, VkImageViewType viewType, VkComponentMapping swizzle); + VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount); struct ImageViewKey { VkImageSubresourceRange range; // 4 * 5 bytes @@ -64,9 +64,10 @@ struct VulkanTextureState : public VulkanResource { std::unique_ptr mSidecarMSAA; VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE; - const VkFormat mVkFormat; - const VkImageViewType mViewType; - const VkComponentMapping mSwizzle; + VkFormat const mVkFormat; + VkImageViewType const mViewType; + VkImageSubresourceRange const mFullViewRange; + VkImage mTextureImage = VK_NULL_HANDLE; // Track the image layout of each subresource using a sparse range map. @@ -88,8 +89,7 @@ struct VulkanTexture : public HwTexture, VulkanResource { VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false, - VkComponentMapping swizzle = {}); + TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false); // Specialized constructor for internally created textures (e.g. from a swap chain) // The texture will never destroy the given VkImage, but it does manages its subresources. @@ -99,12 +99,17 @@ struct VulkanTexture : public HwTexture, VulkanResource { VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false); - // constructor for creating a texture view + // Constructor for creating a texture view for wrt specific mip range + VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, + VmaAllocator allocator, VulkanCommands* commands, + VulkanResourceAllocator* handleAllocator, + VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount); + + // Constructor for creating a texture view for swizzle. VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, - VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount, - VulkanStagePool& stagePool); + VulkanTexture const* src, VkComponentMapping swizzle); ~VulkanTexture(); @@ -115,7 +120,7 @@ struct VulkanTexture : public HwTexture, VulkanResource { // Returns the primary image view, which is used for shader sampling. VkImageView getPrimaryImageView() { VulkanTextureState* state = getSharedState(); - return getImageView(mPrimaryViewRange, state->mViewType, state->mSwizzle); + return getImageView(mPrimaryViewRange, state->mViewType, mSwizzle); } VkImageViewType getViewType() const { @@ -125,8 +130,6 @@ struct VulkanTexture : public HwTexture, VulkanResource { VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; } - VkImageSubresourceRange getFullViewRange() const { return mFullViewRange; } - VulkanLayout getPrimaryImageLayout() const { return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel); } @@ -211,11 +214,11 @@ struct VulkanTexture : public HwTexture, VulkanResource { Handle mState; - VkImageSubresourceRange mFullViewRange; - // Track the range of subresources that define the "primary" image view, which is the special // image view that gets bound to an actual texture sampler. VkImageSubresourceRange mPrimaryViewRange; + + VkComponentMapping mSwizzle {}; }; } // namespace filament::backend diff --git a/filament/backend/src/vulkan/VulkanUtility.cpp b/filament/backend/src/vulkan/VulkanUtility.cpp index 3b8e5f851a2..889e2bdd2f3 100644 --- a/filament/backend/src/vulkan/VulkanUtility.cpp +++ b/filament/backend/src/vulkan/VulkanUtility.cpp @@ -577,7 +577,7 @@ uint32_t getComponentCount(VkFormat format) { return {}; } -VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]) { +VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]) { VkComponentMapping map; VkComponentSwizzle* dst = &map.r; for (int i = 0; i < 4; ++i, ++dst) { diff --git a/filament/backend/src/vulkan/VulkanUtility.h b/filament/backend/src/vulkan/VulkanUtility.h index e0de22cf4dc..43ba73b6db4 100644 --- a/filament/backend/src/vulkan/VulkanUtility.h +++ b/filament/backend/src/vulkan/VulkanUtility.h @@ -38,7 +38,7 @@ VkCullModeFlags getCullMode(CullingMode mode); VkFrontFace getFrontFace(bool inverseFrontFaces); PixelDataType getComponentType(VkFormat format); uint32_t getComponentCount(VkFormat format); -VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]); +VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]); VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags); bool equivalent(const VkRect2D& a, const VkRect2D& b); From fad5d57053defb7ff33806a691dd0cdfb7c66d03 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Fri, 30 Aug 2024 14:39:57 -0700 Subject: [PATCH 30/49] vk: properly compose swizzle --- filament/backend/src/vulkan/VulkanTexture.cpp | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index e40258884bf..1b288d7179b 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -46,6 +46,57 @@ inline uint8_t getLayerCount(SamplerType const target, uint32_t const depth) { } } +VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMapping const& next) { + static constexpr VkComponentSwizzle IDENTITY[] = { + VK_COMPONENT_SWIZZLE_R, + VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, + VK_COMPONENT_SWIZZLE_A, + }; + + auto const compose = [](VkComponentSwizzle out, VkComponentMapping const& prev, + uint8_t channelIndex) { + // We need to first change all identities to its equivalent channel. + if (out == VK_COMPONENT_SWIZZLE_IDENTITY) { + out = IDENTITY[channelIndex]; + } + switch (out) { + case VK_COMPONENT_SWIZZLE_R: + out = prev.r; + break; + case VK_COMPONENT_SWIZZLE_G: + out = prev.g; + break; + case VK_COMPONENT_SWIZZLE_B: + out = prev.b; + break; + case VK_COMPONENT_SWIZZLE_A: + out = prev.a; + break; + case VK_COMPONENT_SWIZZLE_IDENTITY: + case VK_COMPONENT_SWIZZLE_ZERO: + case VK_COMPONENT_SWIZZLE_ONE: + return out; + // Below is not exposed in Vulkan's API, but needs to be there for compilation. + case VK_COMPONENT_SWIZZLE_MAX_ENUM: + break; + } + // If the result correctly corresponds to the identity, just return identity. + if (IDENTITY[channelIndex] == out) { + return VK_COMPONENT_SWIZZLE_IDENTITY; + } + return out; + }; + + // Note that the channel index corresponds to the VkComponentMapping struct layout. + return { + compose(next.r, prev, 0), + compose(next.g, prev, 1), + compose(next.b, prev, 2), + compose(next.a, prev, 3), + }; +} + } // anonymous namespace VulkanTextureState::VulkanTextureState( @@ -275,7 +326,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, auto* state = getSharedState(); state->refs++; mPrimaryViewRange = src->mPrimaryViewRange; - mSwizzle = swizzle; + mSwizzle = composeSwizzle(src->mSwizzle, swizzle); } VulkanTexture::~VulkanTexture() { From 6790a1238bff9ec4ee6e26871c6e55ad02f4c208 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 30 Aug 2024 16:28:14 -0700 Subject: [PATCH 31/49] fix webgl build --- filament/backend/src/opengl/GLDescriptorSet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/filament/backend/src/opengl/GLDescriptorSet.cpp b/filament/backend/src/opengl/GLDescriptorSet.cpp index 65eb275d810..eb54fc90996 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.cpp +++ b/filament/backend/src/opengl/GLDescriptorSet.cpp @@ -222,10 +222,12 @@ void GLDescriptorSet::updateTextureView(OpenGLContext& gl, if (UTILS_UNLIKELY(desc.swizzle != ref->swizzle)) { using namespace GLUtils; gl.activeTexture(unit); +#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2) glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_R, (GLint)getSwizzleChannel(desc.swizzle[0])); glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_G, (GLint)getSwizzleChannel(desc.swizzle[1])); glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_B, (GLint)getSwizzleChannel(desc.swizzle[2])); glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_A, (GLint)getSwizzleChannel(desc.swizzle[3])); +#endif ref->swizzle = desc.swizzle; } } From 6a64cdb059f3b668102b3fef4b65c0bf5565c3f6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 3 Sep 2024 12:14:10 -0700 Subject: [PATCH 32/49] fix build due to a bad merge --- filament/src/details/Renderer.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index c4616ed50d1..35e3ffe8390 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -1021,10 +1021,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { { .width = svp.width, .height = svp.height }); if (UTILS_LIKELY(reflections)) { - fg.addTrivialSideEffectPass("SSR Cleanup", [&view](DriverApi& driver) { - view.getPerViewUniforms().prepareStructure({}); - view.commitUniforms(driver); - }); // generate the mipchain PostProcessManager::generateMipmapSSR(ppm, fg, reflections, ssrConfig.reflection, false, ssrConfig); @@ -1142,12 +1138,6 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { } } - fg.addTrivialSideEffectPass("Finish Color Passes", [&view](DriverApi& driver) { - // Unbind SSAO sampler, b/c the FrameGraph will delete the texture at the end of the pass. - view.cleanupRenderPasses(); - view.commitUniforms(driver); - }); - if (colorGradingConfig.customResolve) { assert_invariant(fg.getDescriptor(colorPassOutput.linearColor).samples <= 1); // TODO: we have to "uncompress" (i.e. detonemap) the color buffer here because it's used From 15d77ed1d7c893d1a0ab4be2cdaa62cfdb15a56c Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Thu, 5 Sep 2024 11:38:10 -0700 Subject: [PATCH 33/49] Metal: respect the shader stage for each descriptor --- filament/backend/src/metal/MetalContext.h | 4 +- filament/backend/src/metal/MetalDriver.mm | 15 +++++-- filament/backend/src/metal/MetalHandles.h | 38 ++++++++++++----- filament/backend/src/metal/MetalHandles.mm | 27 ++++++++----- filament/backend/src/metal/MetalState.h | 2 +- filament/backend/src/metal/MetalState.mm | 47 +++++++++++++++------- filament/backend/test/ShaderGenerator.cpp | 3 +- libs/filamat/src/GLSLPostProcessor.cpp | 23 +++++++---- libs/filamat/src/GLSLPostProcessor.h | 5 ++- 9 files changed, 111 insertions(+), 53 deletions(-) diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 5ab2fccd274..35d3f4434b4 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -185,7 +185,9 @@ struct MetalContext { tsl::robin_set finalizedSamplerGroups; tsl::robin_set finalizedDescriptorSets; std::array currentDescriptorSets = {}; - MetalBufferBindings descriptorBindings; + MetalBufferBindings vertexDescriptorBindings; + MetalBufferBindings fragmentDescriptorBindings; + MetalBufferBindings computeDescriptorBindings; MetalDynamicOffsets dynamicOffsets; // Keeps track of all alive sampler groups, textures. diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 5e29eda4c9e..d18a59bf4be 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1351,7 +1351,9 @@ mContext->finalizedSamplerGroups.clear(); mContext->finalizedDescriptorSets.clear(); - mContext->descriptorBindings.invalidate(); + mContext->vertexDescriptorBindings.invalidate(); + mContext->fragmentDescriptorBindings.invalidate(); + mContext->computeDescriptorBindings.invalidate(); mContext->dynamicOffsets.setDirty(true); // Finalize any descriptor sets that were bound before the render pass. @@ -1365,7 +1367,8 @@ } // Bind descriptor sets. - mContext->descriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + mContext->vertexDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + mContext->fragmentDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); for (auto& pc : mContext->currentPushConstants) { pc.clear(); @@ -2115,7 +2118,10 @@ // Bind the descriptor set. mContext->currentDescriptorSets[set] = descriptorSet; - mContext->descriptorBindings.setBuffer(descriptorSet->finalizeAndGetBuffer(this), 0, set); + mContext->vertexDescriptorBindings.setBuffer( + descriptorSet->finalizeAndGetBuffer(this, ShaderStage::VERTEX), 0, set); + mContext->fragmentDescriptorBindings.setBuffer( + descriptorSet->finalizeAndGetBuffer(this, ShaderStage::FRAGMENT), 0, set); mContext->dynamicOffsets.setOffsets(set, offsets.data(), dynamicBindings); // If we're inside a render pass, we should also finalize the descriptor set and update the @@ -2126,7 +2132,8 @@ descriptorSet->finalize(this); mContext->finalizedDescriptorSets.insert(descriptorSet); } - mContext->descriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + mContext->vertexDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + mContext->fragmentDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); } } diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 398b26ff617..820bf103332 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -554,17 +554,21 @@ class MetalDescriptorSetLayout : public HwDescriptorSetLayout { size_t getDynamicOffsetCount() const noexcept { return mDynamicOffsetCount; } - id getArgumentEncoderForTextureTypes( - id device, utils::FixedCapacityVector const& textureTypes); + /** + * Get an argument encoder for this descriptor set and shader stage. + * textureTypes should only include the textures present in the corresponding shader stage. + */ + id getArgumentEncoder(id device, ShaderStage stage, + utils::FixedCapacityVector const& textureTypes); private: - id getArgumentEncoderForTextureTypesSlow( - id device, utils::FixedCapacityVector const& textureTypes); + id getArgumentEncoderSlow(id device, ShaderStage stage, + utils::FixedCapacityVector const& textureTypes); DescriptorSetLayout mLayout; size_t mDynamicOffsetCount = 0; - id mCachedArgumentEncoder = nil; - utils::FixedCapacityVector mCachedTextureTypes; + std::array, 3> mCachedArgumentEncoder = { nil }; + std::array, 3> mCachedTextureTypes; }; struct MetalDescriptorSet : public HwDescriptorSet { @@ -596,15 +600,23 @@ struct MetalDescriptorSet : public HwDescriptorSet { } } - id finalizeAndGetBuffer(MetalDriver* driver) { + id finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage) { + auto const index = static_cast(stage); + assert_invariant(index < 3); + auto& buffer = cachedBuffer[index]; + if (buffer) { return buffer; } // Map all the texture bindings to their respective texture types. auto const& bindings = layout->getBindings(); - auto textureTypes = utils::FixedCapacityVector::with_capacity(bindings.size()); + auto textureTypes = + utils::FixedCapacityVector::with_capacity(bindings.size()); for (auto const& binding : bindings) { + if (!hasShaderType(binding.stageFlags, stage)) { + continue; + } MTLTextureType textureType = MTLTextureType2D; if (auto found = textures.find(binding.binding); found != textures.end()) { auto const& textureBinding = textures[binding.binding]; @@ -616,13 +628,16 @@ struct MetalDescriptorSet : public HwDescriptorSet { MetalContext const& context = *driver->mContext; id encoder = - layout->getArgumentEncoderForTextureTypes(context.device, textureTypes); + layout->getArgumentEncoder(context.device, stage, textureTypes); buffer = [context.device newBufferWithLength:encoder.encodedLength options:MTLResourceStorageModeShared]; [encoder setArgumentBuffer:buffer offset:0]; for (auto const& binding : bindings) { + if (!hasShaderType(binding.stageFlags, stage)) { + continue; + } switch (binding.type) { case DescriptorType::UNIFORM_BUFFER: case DescriptorType::SHADER_STORAGE_BUFFER: { @@ -657,7 +672,8 @@ struct MetalDescriptorSet : public HwDescriptorSet { SamplerState samplerState { .samplerParams = textureBinding.sampler }; - id sampler = driver->mContext->samplerStateCache.getOrCreateState(samplerState); + id sampler = + driver->mContext->samplerStateCache.getOrCreateState(samplerState); [encoder setSamplerState:sampler atIndex:binding.binding * 2 + 1]; break; @@ -690,7 +706,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { std::vector> externalImages; - id buffer = nil; + std::array, 3> cachedBuffer = { nil }; }; } // namespace backend diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 5a23e460846..9e4cae70220 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -1329,25 +1329,30 @@ static void func(void* user) { mDynamicOffsetCount = dynamicBindings; } -id MetalDescriptorSetLayout::getArgumentEncoderForTextureTypes( - id device, +id MetalDescriptorSetLayout::getArgumentEncoder(id device, ShaderStage stage, utils::FixedCapacityVector const& textureTypes) { - if (mCachedArgumentEncoder && - std::equal(textureTypes.begin(), textureTypes.end(), mCachedTextureTypes.begin())) { - return mCachedArgumentEncoder; - } - mCachedArgumentEncoder = getArgumentEncoderForTextureTypesSlow(device, textureTypes); - mCachedTextureTypes = textureTypes; - return mCachedArgumentEncoder; + auto const index = static_cast(stage); + assert_invariant(index < 3); + if (mCachedArgumentEncoder[index] && + std::equal( + textureTypes.begin(), textureTypes.end(), mCachedTextureTypes[index].begin())) { + return mCachedArgumentEncoder[index]; + } + mCachedArgumentEncoder[index] = getArgumentEncoderSlow(device, stage, textureTypes); + mCachedTextureTypes[index] = textureTypes; + return mCachedArgumentEncoder[index]; } -id MetalDescriptorSetLayout::getArgumentEncoderForTextureTypesSlow( - id device, utils::FixedCapacityVector const& textureTypes) { +id MetalDescriptorSetLayout::getArgumentEncoderSlow(id device, + ShaderStage stage, utils::FixedCapacityVector const& textureTypes) { auto const& bindings = getBindings(); NSMutableArray* arguments = [NSMutableArray new]; // important! the bindings must be sorted by binding number size_t textureIndex = 0; for (auto const& binding : bindings) { + if (!hasShaderType(binding.stageFlags, stage)) { + continue; + } switch (binding.type) { case DescriptorType::UNIFORM_BUFFER: case DescriptorType::SHADER_STORAGE_BUFFER: { diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index 2f5579d060b..bb63fbf5d59 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -416,7 +416,7 @@ struct ArgumentEncoderCreator { using ArgumentEncoderCache = StateCache, ArgumentEncoderCreator, ArgumentEncoderHasher>; -template +template class MetalBufferBindings { public: MetalBufferBindings() { invalidate(); } diff --git a/filament/backend/src/metal/MetalState.mm b/filament/backend/src/metal/MetalState.mm index 104668afa3e..6d42de61850 100644 --- a/filament/backend/src/metal/MetalState.mm +++ b/filament/backend/src/metal/MetalState.mm @@ -208,8 +208,8 @@ return [device newArgumentEncoderWithArguments:arguments]; } -template -void MetalBufferBindings::setBuffer(const id buffer, NSUInteger offset, NSUInteger index) { +template +void MetalBufferBindings::setBuffer(const id buffer, NSUInteger offset, NSUInteger index) { assert_invariant(offset + 1 <= N); if (mBuffers[index] != buffer) { @@ -223,33 +223,50 @@ } } -template -void MetalBufferBindings::bindBuffers(id encoder, NSUInteger startIndex) { +template +void MetalBufferBindings::bindBuffers( + id encoder, NSUInteger startIndex) { if (mDirtyBuffers.none() && mDirtyOffsets.none()) { return; } utils::bitset8 onlyOffsetDirty = mDirtyOffsets & ~mDirtyBuffers; onlyOffsetDirty.forEachSetBit([&](size_t i) { - [(id)encoder setFragmentBufferOffset:mOffsets[i] - atIndex:i + startIndex]; - [(id)encoder setVertexBufferOffset:mOffsets[i] - atIndex:i + startIndex]; + if constexpr (stage == ShaderStage::VERTEX) { + [(id)encoder setVertexBufferOffset:mOffsets[i] + atIndex:i + startIndex]; + } else if constexpr (stage == ShaderStage::FRAGMENT) { + [(id)encoder setFragmentBufferOffset:mOffsets[i] + atIndex:i + startIndex]; + } else if constexpr (stage == ShaderStage::COMPUTE) { + [(id)encoder setBufferOffset:mOffsets[i] + atIndex:i + startIndex]; + } }); mDirtyOffsets.reset(); mDirtyBuffers.forEachSetBit([&](size_t i) { - [(id)encoder setFragmentBuffer:mBuffers[i] - offset:mOffsets[i] - atIndex:i + startIndex]; - [(id)encoder setVertexBuffer:mBuffers[i] - offset:mOffsets[i] - atIndex:i + startIndex]; + if constexpr (stage == ShaderStage::VERTEX) { + [(id)encoder setVertexBuffer:mBuffers[i] + offset:mOffsets[i] + atIndex:i + startIndex]; + } else if constexpr (stage == ShaderStage::FRAGMENT) { + [(id)encoder setFragmentBuffer:mBuffers[i] + offset:mOffsets[i] + atIndex:i + startIndex]; + } else if constexpr (stage == ShaderStage::COMPUTE) { + [(id)encoder setBuffer:mBuffers[i] + offset:mOffsets[i] + atIndex:i + startIndex]; + } }); mDirtyBuffers.reset(); } -template class MetalBufferBindings<4>; +// TODO: don't use constant 4 +template class MetalBufferBindings<4, ShaderStage::VERTEX>; +template class MetalBufferBindings<4, ShaderStage::FRAGMENT>; +template class MetalBufferBindings<4, ShaderStage::COMPUTE>; } // namespace backend } // namespace filament diff --git a/filament/backend/test/ShaderGenerator.cpp b/filament/backend/test/ShaderGenerator.cpp index d953f53d9f6..545a6f97089 100644 --- a/filament/backend/test/ShaderGenerator.cpp +++ b/filament/backend/test/ShaderGenerator.cpp @@ -161,7 +161,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s return { result.c_str(), result.c_str() + result.length() + 1 }; } else if (backend == Backend::METAL) { const auto sm = isMobile ? ShaderModel::MOBILE : ShaderModel::DESKTOP; - filamat::GLSLPostProcessor::spirvToMsl(&spirv, &result, sm, false, descriptorSets, nullptr); + filamat::GLSLPostProcessor::spirvToMsl( + &spirv, &result, stage, sm, false, descriptorSets, nullptr); return { result.c_str(), result.c_str() + result.length() + 1 }; } else if (backend == Backend::VULKAN) { return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) }; diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 38d8f16718b..77d13ae9622 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -387,8 +387,9 @@ static std::string stringifySpvOptimizerMessage(spv_message_level_t level, const } void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, - filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, - const DescriptorSets& descriptorSets, const ShaderMinifier* minifier) { + filament::backend::ShaderStage stage, filament::backend::ShaderModel shaderModel, + bool useFramebufferFetch, const DescriptorSets& descriptorSets, + const ShaderMinifier* minifier) { using namespace msl; CompilerMSL mslCompiler(*spirv); @@ -556,6 +557,13 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, "spvDescriptorSetBuffer" + std::to_string(int(setIndex))); for (auto const& descriptor : descriptors) { auto const& [name, info, sampler] = descriptor; + if (!hasShaderType(info.stageFlags, stage)) { + if (any(info.flags & DescriptorFlags::DYNAMIC_OFFSET)) { + // We still need to increment the dynamic offset index + dynamicOffsetsBufferIndex++; + } + continue; + } switch (info.type) { case DescriptorType::INPUT_ATTACHMENT: // TODO: Handle INPUT_ATTACHMENT case @@ -707,7 +715,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co msl::prettyPrintDescriptorSetInfoVector(descriptors); #endif spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, - config.shaderModel, config.hasFramebufferFetch, descriptors, + config.shaderType, config.shaderModel, config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } } else { @@ -798,8 +806,8 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, #if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); #endif - spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderModel, - config.hasFramebufferFetch, descriptors, + spirvToMsl(internalConfig.spirvOutput, internalConfig.mslOutput, config.shaderType, + config.shaderModel, config.hasFramebufferFetch, descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } @@ -842,8 +850,9 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, #if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); #endif - spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderModel, config.hasFramebufferFetch, - descriptors, mGenerateDebugInfo ? &internalConfig.minifier : nullptr); + spirvToMsl(&spirv, internalConfig.mslOutput, config.shaderType, config.shaderModel, + config.hasFramebufferFetch, descriptors, + mGenerateDebugInfo ? &internalConfig.minifier : nullptr); } // Transpile back to GLSL diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index b873a383ca1..8616d0e4dc8 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -85,8 +85,9 @@ class GLSLPostProcessor { // public so backend_test can also use it static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, - filament::backend::ShaderModel shaderModel, bool useFramebufferFetch, - const DescriptorSets& descriptorSets, const ShaderMinifier* minifier); + filament::backend::ShaderStage stage, filament::backend::ShaderModel shaderModel, + bool useFramebufferFetch, const DescriptorSets& descriptorSets, + const ShaderMinifier* minifier); private: struct InternalConfig { From 91ce3a9375650e51046f081dea4b1e4881327eb0 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Fri, 6 Sep 2024 16:54:38 -0700 Subject: [PATCH 34/49] Smaller empty buffer --- filament/backend/src/metal/MetalDriver.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index d18a59bf4be..c86bf307100 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -122,7 +122,7 @@ mContext->device = mPlatform.createDevice(); assert_invariant(mContext->device); - mContext->emptyBuffer = [mContext->device newBufferWithLength:4 * 1024 + mContext->emptyBuffer = [mContext->device newBufferWithLength:16 options:MTLResourceStorageModePrivate]; initializeSupportedGpuFamilies(mContext); From f6d9db14c3cf7b83f46380cd6b04bd678841941f Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Fri, 6 Sep 2024 17:12:56 -0700 Subject: [PATCH 35/49] Better DEBUG_LOG --- filament/backend/src/metal/MetalDriver.mm | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index c86bf307100..501bb77865b 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -41,12 +41,12 @@ #include -#ifndef DEBUG_LOG_DESCRIPTOR_SETS -#define DEBUG_LOG_DESCRIPTOR_SETS 0 +#ifndef FILAMENT_METAL_DEBUG_LOG +#define FILAMENT_METAL_DEBUG_LOG 0 #endif -#if DEBUG_LOG_DESCRIPTOR_SETS == 1 -#define DEBUG_LOG(...) printf(__VA_ARGS__) +#if FILAMENT_METAL_DEBUG_LOG == 1 +#define DEBUG_LOG(x, ...) printf("[METAL DEBUG] " x, ##__VA_ARGS__) #else #define DEBUG_LOG(...) #endif @@ -240,8 +240,7 @@ void MetalDriver::beginFrame(int64_t monotonic_clock_ns, int64_t refreshIntervalNs, uint32_t frameId) { - DEBUG_LOG( - "[DS] beginFrame(monotonic_clock_ns = %lld, refreshIntervalNs = %lld, frameId = %d)\n", + DEBUG_LOG("beginFrame(monotonic_clock_ns = %lld, refreshIntervalNs = %lld, frameId = %d)\n", monotonic_clock_ns, refreshIntervalNs, frameId); #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_begin(mContext->log, mContext->signpostId, "Frame encoding", "%{public}d", frameId); @@ -279,7 +278,7 @@ } void MetalDriver::endFrame(uint32_t frameId) { - DEBUG_LOG("[DS] endFrame(frameId = %d)\n", frameId); + DEBUG_LOG("endFrame(frameId = %d)\n", frameId); // If we haven't committed the command buffer (if the frame was canceled), do it now. There may // be commands in it (like fence signaling) that need to execute. submitPendingCommands(mContext); @@ -321,7 +320,7 @@ ASSERT_PRECONDITION(!isInRenderPass(mContext), "updateDescriptorSetBuffer must be called outside of a render pass."); DEBUG_LOG( - "[DS] updateDescriptorSetBuffer(dsh = %d, binding = %d, boh = %d, offset = %d, size = " + "updateDescriptorSetBuffer(dsh = %d, binding = %d, boh = %d, offset = %d, size = " "%d)\n", dsh.getId(), binding, boh.getId(), offset, size); @@ -345,7 +344,7 @@ SamplerParams params) { ASSERT_PRECONDITION(!isInRenderPass(mContext), "updateDescriptorSetTexture must be called outside of a render pass."); - DEBUG_LOG("[DS] updateDescriptorSetTexture(dsh = %d, binding = %d, th = %d, params = {...})\n", + DEBUG_LOG("updateDescriptorSetTexture(dsh = %d, binding = %d, th = %d, params = {...})\n", dsh.getId(), binding, th.getId()); auto* descriptorSet = handle_cast(dsh); @@ -650,7 +649,7 @@ void MetalDriver::createDescriptorSetLayoutR( Handle dslh, DescriptorSetLayout&& info) { - DEBUG_LOG("[DS] createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); + DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); for (size_t i = 0; i < info.bindings.size(); i++) { DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s}", info.bindings[i].binding, prettyDescriptorType(info.bindings[i].type), @@ -666,7 +665,7 @@ void MetalDriver::createDescriptorSetR( Handle dsh, Handle dslh) { - DEBUG_LOG("[DS] createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId()); + DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId()); MetalDescriptorSetLayout* layout = handle_cast(dslh); construct_handle(dsh, layout); } @@ -878,14 +877,14 @@ } void MetalDriver::destroyDescriptorSetLayout(Handle dslh) { - DEBUG_LOG("[DS] destroyDescriptorSetLayout(dslh = %d)\n", dslh.getId()); + DEBUG_LOG("destroyDescriptorSetLayout(dslh = %d)\n", dslh.getId()); if (dslh) { destruct_handle(dslh); } } void MetalDriver::destroyDescriptorSet(Handle dsh) { - DEBUG_LOG("[DS] destroyDescriptorSet(dsh = %d)\n", dsh.getId()); + DEBUG_LOG("destroyDescriptorSet(dsh = %d)\n", dsh.getId()); if (dsh) { executeAfterCurrentCommandBufferCompletes( [this, dsh]() mutable { destruct_handle(dsh); }); @@ -1307,7 +1306,7 @@ void MetalDriver::beginRenderPass(Handle rth, const RenderPassParams& params) { - DEBUG_LOG("[DS] beginRenderPass(rth = %d, params = {...})\n", rth.getId()); + DEBUG_LOG("beginRenderPass(rth = %d, params = {...})\n", rth.getId()); #if defined(FILAMENT_METAL_PROFILING) const char* renderPassName = "Unknown"; @@ -1378,7 +1377,7 @@ void MetalDriver::nextSubpass(int dummy) {} void MetalDriver::endRenderPass(int dummy) { - DEBUG_LOG("[DS] endRenderPass()\n"); + DEBUG_LOG("endRenderPass()\n"); #if defined(FILAMENT_METAL_PROFILING) os_signpost_interval_end(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass"); #endif @@ -2105,7 +2104,7 @@ auto descriptorSet = handle_cast(dsh); const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount(); utils::FixedCapacityVector offsetsVector(dynamicBindings, 0); - DEBUG_LOG("[DS] bindDescriptorSet(dsh = %d, set = %d, offsets = [", dsh.getId(), set); + DEBUG_LOG("bindDescriptorSet(dsh = %d, set = %d, offsets = [", dsh.getId(), set); for (size_t i = 0; i < dynamicBindings; i++) { DEBUG_LOG("%d", offsets[i]); if (i < dynamicBindings - 1) { @@ -2140,7 +2139,7 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) << "draw() without a valid command encoder."; - DEBUG_LOG("[DS] draw2(...)\n"); + DEBUG_LOG("draw2(...)\n"); // Bind the offset data. if (mContext->dynamicOffsets.isDirty()) { From c538bded6f35847a7da6f5f4fb8111498fbf8b83 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 12:07:16 -0700 Subject: [PATCH 36/49] Get rid of unused MetalSamplerGroup and related code --- filament/backend/src/metal/MetalContext.h | 9 +- filament/backend/src/metal/MetalDriver.h | 1 - filament/backend/src/metal/MetalDriver.mm | 234 +-------------------- filament/backend/src/metal/MetalHandles.h | 81 ------- filament/backend/src/metal/MetalHandles.mm | 82 -------- 5 files changed, 3 insertions(+), 404 deletions(-) diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 35d3f4434b4..49dfa272d62 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -48,7 +48,6 @@ class MetalBlitter; class MetalBufferPool; class MetalBumpAllocator; class MetalRenderTarget; -class MetalSamplerGroup; class MetalSwapChain; class MetalTexture; class MetalTimerQueryInterface; @@ -179,10 +178,7 @@ struct MetalContext { std::array currentPushConstants; - MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {}; - - // Keeps track of sampler groups we've finalized for the current render pass. - tsl::robin_set finalizedSamplerGroups; + // Keeps track of descriptor sets we've finalized for the current render pass. tsl::robin_set finalizedDescriptorSets; std::array currentDescriptorSets = {}; MetalBufferBindings vertexDescriptorBindings; @@ -190,8 +186,7 @@ struct MetalContext { MetalBufferBindings computeDescriptorBindings; MetalDynamicOffsets dynamicOffsets; - // Keeps track of all alive sampler groups, textures. - tsl::robin_set samplerGroups; + // Keeps track of all alive textures. tsl::robin_set textures; // This circular buffer implements delayed destruction for Metal texture handles. It keeps a diff --git a/filament/backend/src/metal/MetalDriver.h b/filament/backend/src/metal/MetalDriver.h index 7190c518b81..7347a42a76d 100644 --- a/filament/backend/src/metal/MetalDriver.h +++ b/filament/backend/src/metal/MetalDriver.h @@ -151,7 +151,6 @@ class MetalDriver final : public DriverBase { inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt, Handle vbh, Handle ibh); - void finalizeSamplerGroup(MetalSamplerGroup* sg); void enumerateBoundBuffers(BufferObjectBinding bindingType, const std::function& f); diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 501bb77865b..b1916e4fe91 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -67,7 +67,6 @@ // MetalIndexBuffer : 56 moderate // MetalBufferObject : 64 many // -- less than or equal 64 bytes - // MetalSamplerGroup : 112 few // MetalProgram : 152 moderate // MetalTexture : 152 moderate // MetalSwapChain : 208 few @@ -81,7 +80,6 @@ << "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer) << "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo) << "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer) - << "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup) << "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive) << "\nMetalTexture: " << sizeof(MetalTexture) << "\nMetalTimerQuery: " << sizeof(MetalTimerQuery) @@ -513,7 +511,6 @@ void MetalDriver::createSamplerGroupR( Handle sbh, uint32_t size, utils::FixedSizeString<32> debugName) { - mContext->samplerGroups.insert(construct_handle(sbh, size, debugName)); } void MetalDriver::createRenderPrimitiveR(Handle rph, @@ -711,7 +708,7 @@ } Handle MetalDriver::createSamplerGroupS() noexcept { - return alloc_handle(); + return {}; } Handle MetalDriver::createRenderPrimitiveS() noexcept { @@ -812,18 +809,6 @@ } void MetalDriver::destroySamplerGroup(Handle sbh) { - if (!sbh) { - return; - } - // Unbind this sampler group from our internal state. - auto* metalSampler = handle_cast(sbh); - for (auto& samplerBinding : mContext->samplerBindings) { - if (samplerBinding == metalSampler) { - samplerBinding = {}; - } - } - mContext->samplerGroups.erase(metalSampler); - destruct_handle(sbh); } void MetalDriver::destroyTexture(Handle th) { @@ -1208,93 +1193,6 @@ } void MetalDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { - FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext)) - << "updateSamplerGroup must be called outside of a render pass."; - - auto sb = handle_cast(sbh); - assert_invariant(sb->size == data.size / sizeof(SamplerDescriptor)); - auto const* const samplers = (SamplerDescriptor const*) data.buffer; - - // Verify that all the textures in the sampler group are still alive. - // These bugs lead to memory corruption and can be difficult to track down. - for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) { - if (!samplers[s].t) { - continue; - } - // The difference between this check and the one below is that in release, we do this for - // only a set number of recently freed textures, while the debug check is exhaustive. - auto* metalTexture = handle_cast(samplers[s].t); - metalTexture->checkUseAfterFree(sb->debugName.c_str(), s); -#ifndef NDEBUG - auto iter = mContext->textures.find(handle_cast(samplers[s].t)); - if (iter == mContext->textures.end()) { - utils::slog.e << "updateSamplerGroup: texture #" - << (int) s << " is dead, texture handle = " - << samplers[s].t << utils::io::endl; - } - assert_invariant(iter != mContext->textures.end()); -#endif - } - - // Create a MTLArgumentEncoder for these textures. - // Ideally, we would create this encoder at createSamplerGroup time, but we need to know the - // texture type of each texture. It's also not guaranteed that the types won't change between - // calls to updateSamplerGroup. - utils::FixedCapacityVector textureTypes(sb->size); - std::transform(samplers, samplers + data.size / sizeof(SamplerDescriptor), textureTypes.begin(), - [this](const SamplerDescriptor& sampler) { - if (!sampler.t) { - // Use Type2D for non-bound textures. - return MTLTextureType2D; - } - auto* t = handle_cast(sampler.t); - if (t->target == SamplerType::SAMPLER_EXTERNAL) { - // Use Type2D for external image textures. - return MTLTextureType2D; - } - id mtlTexture = t->getMtlTextureForRead(); - assert_invariant(mtlTexture); - return mtlTexture.textureType; - }); - auto& encoderCache = mContext->argumentEncoderCache; - id encoder = - encoderCache.getOrCreateState(ArgumentEncoderState(0, std::move(textureTypes))); - sb->reset(getPendingCommandBuffer(mContext), encoder, mContext->device); - - // In a perfect world, all the MTLTexture bindings would be known at updateSamplerGroup time. - // However, there are two special cases preventing this: - // 1. External images - // 2. LOD-clamped textures - // - // Both of these cases prevent us from knowing the final id that will be bound into - // the argument buffer representing the sampler group. So, we wait until draw call time to bind - // textures (done in finalizeSamplerGroup). - // The good news is that once a render pass has started, the texture bindings won't change. - // A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in - // a draw call. - // finalizeSamplerGroup has one additional responsibility: to call useResources for all the - // textures, which is required by Metal. - for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) { - if (!samplers[s].t) { - // Assign a default sampler to empty slots. - // Metal requires all samplers referenced in shaders to be bound. - // An empty texture will be assigned inside finalizeSamplerGroup. - id sampler = mContext->samplerStateCache.getOrCreateState({}); - sb->setFinalizedSampler(s, sampler); - continue; - } - - // Bind the sampler state. We always know the full sampler state at updateSamplerGroup time. - SamplerState samplerState { - .samplerParams = samplers[s].s, - }; - id sampler = mContext->samplerStateCache.getOrCreateState(samplerState); - sb->setFinalizedSampler(s, sampler); - - sb->setTextureHandle(s, samplers[s].t); - } - - scheduleDestroy(std::move(data)); } void MetalDriver::compilePrograms(CompilerPriorityQueue priority, @@ -1348,7 +1246,6 @@ mContext->windingState.invalidate(); mContext->currentPolygonOffset = {0.0f, 0.0f}; - mContext->finalizedSamplerGroups.clear(); mContext->finalizedDescriptorSets.clear(); mContext->vertexDescriptorBindings.invalidate(); mContext->fragmentDescriptorBindings.invalidate(); @@ -1477,8 +1374,6 @@ } void MetalDriver::bindSamplers(uint32_t index, Handle sbh) { - auto sb = handle_cast(sbh); - mContext->samplerBindings[index] = sb; } void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, @@ -1784,102 +1679,6 @@ } } -void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) { - // All the id objects have already been bound to the argument buffer. - // Here we bind all the textures. - - id cmdBuffer = getPendingCommandBuffer(mContext); - - // Verify that all the textures in the sampler group are still alive. - // These bugs lead to memory corruption and can be difficult to track down. - const auto& handles = samplerGroup->getTextureHandles(); - for (size_t s = 0; s < handles.size(); s++) { - if (!handles[s]) { - continue; - } - // The difference between this check and the one below is that in release, we do this for - // only a set number of recently freed textures, while the debug check is exhaustive. - auto* metalTexture = handle_cast(handles[s]); - metalTexture->checkUseAfterFree(samplerGroup->debugName.c_str(), s); -#ifndef NDEBUG - auto iter = mContext->textures.find(metalTexture); - if (iter == mContext->textures.end()) { - utils::slog.e << "finalizeSamplerGroup: texture #" - << (int) s << " is dead, texture handle = " - << handles[s] << utils::io::endl; - } - assert_invariant(iter != mContext->textures.end()); -#endif - } - - utils::FixedCapacityVector> newTextures(samplerGroup->size, nil); - for (size_t binding = 0; binding < samplerGroup->size; binding++) { - auto [th, _] = samplerGroup->getFinalizedTexture(binding); - - if (!th) { - // Bind an empty texture. - newTextures[binding] = getOrCreateEmptyTexture(mContext); - continue; - } - - assert_invariant(th); - auto* texture = handle_cast(th); - - // External images - if (texture->target == SamplerType::SAMPLER_EXTERNAL) { - continue; - /* - if (texture->externalImage.isValid()) { - id mtlTexture = texture->externalImage.getMetalTextureForDraw(); - assert_invariant(mtlTexture); - newTextures[binding] = mtlTexture; - } else { - // Bind an empty texture. - newTextures[binding] = getOrCreateEmptyTexture(mContext); - } - continue; - */ - } - - newTextures[binding] = texture->getMtlTextureForRead(); - } - - if (!std::equal(newTextures.begin(), newTextures.end(), samplerGroup->textures.begin())) { - // One or more of the ids has changed. - // First, determine if this SamplerGroup needs mutation. - // We can't just simply mutate the SamplerGroup, since it could currently be in use by the - // GPU from a prior render pass. - // If the SamplerGroup does need mutation, then there's two cases: - // 1. The SamplerGroup has not been finalized yet (which means it has not yet been used in a - // draw call). We're free to mutate it. - // 2. The SamplerGroup is finalized. We must call mutate(), which will create a new argument - // buffer that we can then mutate freely. - - if (samplerGroup->isFinalized()) { - samplerGroup->mutate(cmdBuffer); - } - - for (size_t binding = 0; binding < samplerGroup->size; binding++) { - samplerGroup->setFinalizedTexture(binding, newTextures[binding]); - } - - samplerGroup->finalize(); - } - - // At this point, all the id should be set to valid textures. Some of them will be - // the "empty" texture. Per Apple documentation, the useResource method must be called once per - // render pass. - samplerGroup->useResources(mContext->currentRenderPassEncoder); - - // useResources won't retain references to the textures, so we need to do so manually. - for (id texture : samplerGroup->textures) { - const void* retainedTexture = CFBridgingRetain(texture); - [cmdBuffer addCompletedHandler:^(id cb) { - CFBridgingRelease(retainedTexture); - }]; - } -} - void MetalDriver::bindPipeline(PipelineState const& ps) { FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr) << "bindPipeline() without a valid command encoder."; @@ -2027,37 +1826,6 @@ clamp:0.0]; mContext->currentPolygonOffset = ps.polygonOffset; } - - // Bind sampler groups (argument buffers). - for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) { - MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s]; - if (!samplerGroup) { - continue; - } -// const auto& stageFlags = program->getSamplerGroupInfo()[s].stageFlags; -// if (stageFlags == ShaderStageFlags::NONE) { -// continue; -// } - - auto iter = mContext->finalizedSamplerGroups.find(samplerGroup); - if (iter == mContext->finalizedSamplerGroups.end()) { - finalizeSamplerGroup(samplerGroup); - mContext->finalizedSamplerGroups.insert(samplerGroup); - } - - assert_invariant(samplerGroup->getArgumentBuffer()); - -// if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::VERTEX)) { -// [mContext->currentRenderPassEncoder setVertexBuffer:samplerGroup->getArgumentBuffer() -// offset:samplerGroup->getArgumentBufferOffset() -// atIndex:(SAMPLER_GROUP_BINDING_START + s)]; -// } -// if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::FRAGMENT)) { -// [mContext->currentRenderPassEncoder setFragmentBuffer:samplerGroup->getArgumentBuffer() -// offset:samplerGroup->getArgumentBufferOffset() -// atIndex:(SAMPLER_GROUP_BINDING_START + s)]; -// } - } } void MetalDriver::bindRenderPrimitive(Handle rph) { diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 820bf103332..550063ba535 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -202,12 +202,10 @@ class MetalProgram : public HwProgram { MetalProgram(MetalContext& context, Program&& program) noexcept; const MetalShaderCompiler::MetalFunctionBundle& getFunctions(); -// const Program::SamplerGroupInfo& getSamplerGroupInfo() { return samplerGroupInfo; } private: void initialize(); -// Program::SamplerGroupInfo samplerGroupInfo; MetalContext& mContext; MetalShaderCompiler::MetalFunctionBundle mFunctionBundle; MetalShaderCompiler::program_token_t mToken; @@ -313,85 +311,6 @@ class MetalTexture : public HwTexture { bool terminated = false; }; -class MetalSamplerGroup : public HwSamplerGroup { -public: - explicit MetalSamplerGroup(size_t size, utils::FixedSizeString<32> name) noexcept - : size(size), - debugName(name), - textureHandles(size, Handle()), - textures(size, nil), - samplers(size, nil) {} - - inline void setTextureHandle(size_t index, Handle th) { - assert_invariant(!finalized); - textureHandles[index] = th; - } - - // This method is only used for debugging, to ensure all texture handles are alive. - const auto& getTextureHandles() const { - return textureHandles; - } - - // Encode a MTLTexture into this SamplerGroup at the given index. - inline void setFinalizedTexture(size_t index, id t) { - assert_invariant(!finalized); - textures[index] = t; - } - - // Encode a MTLSamplerState into this SamplerGroup at the given index. - inline void setFinalizedSampler(size_t index, id s) { - assert_invariant(!finalized); - samplers[index] = s; - } - - // A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in - // a draw call. - // Once a SamplerGroup is finalized, it must be reset or mutated to be written into again. - void finalize(); - bool isFinalized() const noexcept { return finalized; } - - // Both of these methods "unfinalize" a SamplerGroup, allowing it to be updated via calls to - // setFinalizedTexture or setFinalizedSampler. The difference is that when reset is called, all - // the samplers/textures must be rebound. The MTLArgumentEncoder must be specified, in case - // the texture types have changed. - // Mutate re-encodes the current set of samplers/textures into the new argument - // buffer. - void reset(id cmdBuffer, id e, id device); - void mutate(id cmdBuffer); - - id getArgumentBuffer() const { - assert_invariant(finalized); - return argBuffer->getCurrentAllocation().first; - } - - NSUInteger getArgumentBufferOffset() const { - return argBuffer->getCurrentAllocation().second; - } - - inline std::pair, id> getFinalizedTexture(size_t index) { - return {textureHandles[index], textures[index]}; - } - - // Calls the Metal useResource:usage:stages: method for all the textures in this SamplerGroup. - void useResources(id renderPassEncoder); - - size_t size; - utils::FixedSizeString<32> debugName; - -public: - - // These vectors are kept in sync with one another. - utils::FixedCapacityVector> textureHandles; - utils::FixedCapacityVector> textures; - utils::FixedCapacityVector> samplers; - - id encoder; - - std::unique_ptr argBuffer = nullptr; - - bool finalized = false; -}; - class MetalRenderTarget : public HwRenderTarget { public: diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 9e4cae70220..c213a115478 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -470,11 +470,6 @@ static void func(void* user) { MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept : HwProgram(program.getName()), mContext(context) { - - // Save this program's SamplerGroupInfo, it's used during draw calls to bind sampler groups to - // the appropriate stage(s). -// samplerGroupInfo = program.getSamplerGroupInfo(); - mToken = context.shaderCompiler->createProgram(program.getName(), std::move(program)); assert_invariant(mToken); } @@ -896,83 +891,6 @@ static void func(void* user) { context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit"); } -void MetalSamplerGroup::finalize() { - assert_invariant(encoder); - // TODO: we should be able to encode textures and samplers inside setFinalizedTexture and - // setFinalizedSampler as they become available, but Metal doesn't seem to like this; the arg - // buffer gets encoded incorrectly. This warrants more investigation. - - auto [buffer, offset] = argBuffer->getCurrentAllocation(); - [encoder setArgumentBuffer:buffer offset:offset]; - - // Encode all textures and samplers. - for (size_t s = 0; s < size; s++) { - [encoder setTexture:textures[s] atIndex:(s * 2 + 0)]; - [encoder setSamplerState:samplers[s] atIndex:(s * 2 + 1)]; - } - - finalized = true; -} - -void MetalSamplerGroup::reset(id cmdBuffer, id e, - id device) { - encoder = e; - - // The number of slots in the ring buffer we use to manage argument buffer allocations. - // This number was chosen to avoid running out of slots and having to allocate a "fallback" - // buffer when SamplerGroups are updated multiple times a frame. This value can reduced after - // auditing Filament's calls to updateSamplerGroup, which should be as few times as possible. - // For example, the bloom downsample pass should be refactored to maintain two separate - // MaterialInstances instead of "ping ponging" between two texture bindings, which causes a - // single SamplerGroup to be updated many times a frame. - static constexpr auto METAL_ARGUMENT_BUFFER_SLOTS = 32; - - MTLSizeAndAlign argBufferLayout; - argBufferLayout.size = encoder.encodedLength; - argBufferLayout.align = encoder.alignment; - // Chances are, even though the MTLArgumentEncoder might change, the required size and alignment - // probably won't. So we can re-use the previous ring buffer. - if (UTILS_UNLIKELY(!argBuffer || !argBuffer->canAccomodateLayout(argBufferLayout))) { - argBuffer = std::make_unique(device, MTLResourceStorageModeShared, - argBufferLayout, METAL_ARGUMENT_BUFFER_SLOTS); - } else { - argBuffer->createNewAllocation(cmdBuffer); - } - - // Clear all textures and samplers. - assert_invariant(textureHandles.size() == textures.size()); - assert_invariant(textures.size() == samplers.size()); - for (size_t s = 0; s < textureHandles.size(); s++) { - textureHandles[s] = {}; - textures[s] = nil; - samplers[s] = nil; - } - - finalized = false; -} - -void MetalSamplerGroup::mutate(id cmdBuffer) { - assert_invariant(finalized); // only makes sense to mutate if this sampler group is finalized - assert_invariant(argBuffer); - argBuffer->createNewAllocation(cmdBuffer); - finalized = false; -} - -void MetalSamplerGroup::useResources(id renderPassEncoder) { - assert_invariant(finalized); - if (@available(iOS 13, *)) { - // TODO: pass only the appropriate stages to useResources. - [renderPassEncoder useResources:textures.data() - count:textures.size() - usage:MTLResourceUsageRead | MTLResourceUsageSample - stages:MTLRenderStageFragment | MTLRenderStageVertex]; - } else { - [renderPassEncoder useResources:textures.data() - count:textures.size() - usage:MTLResourceUsageRead | MTLResourceUsageSample]; - } -} - MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples, Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT], Attachment depthAttachment, Attachment stencilAttachment) : From efb94da111b6ec58e79a5ab76ef56fdb001415c1 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 12:16:50 -0700 Subject: [PATCH 37/49] Remove deprecated driver APIS --- .../include/private/backend/DriverAPI.inc | 23 --- filament/backend/src/metal/MetalContext.h | 2 - filament/backend/src/metal/MetalDriver.mm | 143 ------------------ filament/backend/src/metal/MetalHandles.h | 6 - filament/backend/src/noop/NoopDriver.cpp | 18 --- filament/backend/src/opengl/OpenGLDriver.cpp | 31 ---- filament/backend/src/vulkan/VulkanDriver.cpp | 43 ------ filament/src/RenderPass.cpp | 9 -- 8 files changed, 275 deletions(-) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 857e41db34c..20297b61792 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -238,9 +238,6 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture, uint32_t, depth, backend::TextureUsage, usage) -DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup, - uint32_t, size, utils::FixedSizeString<32>, debugName) - DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive, backend::VertexBufferHandle, vbh, backend::IndexBufferHandle, ibh, @@ -313,7 +310,6 @@ DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh) DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh) DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph) DECL_DRIVER_API_N(destroyProgram, backend::ProgramHandle, ph) -DECL_DRIVER_API_N(destroySamplerGroup, backend::SamplerGroupHandle, sbh) DECL_DRIVER_API_N(destroyTexture, backend::TextureHandle, th) DECL_DRIVER_API_N(destroyRenderTarget, backend::RenderTargetHandle, rth) DECL_DRIVER_API_N(destroySwapChain, backend::SwapChainHandle, sch) @@ -388,10 +384,6 @@ DECL_DRIVER_API_N(updateBufferObjectUnsynchronized, DECL_DRIVER_API_N(resetBufferObject, backend::BufferObjectHandle, ibh) -DECL_DRIVER_API_N(updateSamplerGroup, - backend::SamplerGroupHandle, ubh, - backend::BufferDescriptor&&, data) - DECL_DRIVER_API_N(update3DImage, backend::TextureHandle, th, uint32_t, level, @@ -458,21 +450,6 @@ DECL_DRIVER_API_N(commit, * ----------------------- */ -DECL_DRIVER_API_N(bindUniformBuffer, - uint32_t, index, - backend::BufferObjectHandle, ubh) - -DECL_DRIVER_API_N(bindBufferRange, - BufferObjectBinding, bindingType, - uint32_t, index, - backend::BufferObjectHandle, ubh, - uint32_t, offset, - uint32_t, size) - -DECL_DRIVER_API_N(bindSamplers, - uint32_t, index, - backend::SamplerGroupHandle, sbh) - DECL_DRIVER_API_N(setPushConstant, backend::ShaderStage, stage, uint8_t, index, diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 49dfa272d62..3f6cbaa1a95 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -161,8 +161,6 @@ struct MetalContext { // State trackers. PipelineStateTracker pipelineState; DepthStencilStateTracker depthStencilState; - std::array uniformState; - std::array ssboState; CullModeStateTracker cullModeState; WindingStateTracker windingState; DepthClampStateTracker depthClampState; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index b1916e4fe91..87819d3b183 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -509,10 +509,6 @@ target, levels, format, samples, width, height, depth, usage, metalTexture)); } -void MetalDriver::createSamplerGroupR( - Handle sbh, uint32_t size, utils::FixedSizeString<32> debugName) { -} - void MetalDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, PrimitiveType pt) { @@ -707,10 +703,6 @@ return alloc_handle(); } -Handle MetalDriver::createSamplerGroupS() noexcept { - return {}; -} - Handle MetalDriver::createRenderPrimitiveS() noexcept { return alloc_handle(); } @@ -777,22 +769,6 @@ if (UTILS_UNLIKELY(!boh)) { return; } - auto* bo = handle_cast(boh); - // Unbind this buffer object from any uniform / SSBO slots it's still bound to. - bo->boundUniformBuffers.forEachSetBit([this](size_t index) { - mContext->uniformState[index] = BufferState { - .buffer = nullptr, - .offset = 0, - .bound = false - }; - }); - bo->boundSsbos.forEachSetBit([this](size_t index) { - mContext->ssboState[index] = BufferState { - .buffer = nullptr, - .offset = 0, - .bound = false - }; - }); destruct_handle(boh); } @@ -808,9 +784,6 @@ } } -void MetalDriver::destroySamplerGroup(Handle sbh) { -} - void MetalDriver::destroyTexture(Handle th) { if (!th) { return; @@ -1192,9 +1165,6 @@ DEBUG_LOG("generateMipmaps(th = %d)\n", th.getId()); } -void MetalDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { -} - void MetalDriver::compilePrograms(CompilerPriorityQueue priority, CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { if (callback) { @@ -1314,68 +1284,6 @@ swapChain->releaseDrawable(); } -void MetalDriver::bindUniformBuffer(uint32_t index, Handle boh) { - assert_invariant(index < Program::UNIFORM_BINDING_COUNT); - auto* bo = handle_cast(boh); - auto* currentBo = mContext->uniformState[index].buffer; - if (currentBo) { - currentBo->boundUniformBuffers.unset(index); - } - bo->boundUniformBuffers.set(index); - mContext->uniformState[index] = BufferState{ - .buffer = bo, - .offset = 0, - .bound = true - }; -} - -void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index, - Handle boh, uint32_t offset, uint32_t size) { - - assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE || - bindingType == BufferObjectBinding::UNIFORM); - - auto* bo = handle_cast(boh); - - switch (bindingType) { - default: - case BufferObjectBinding::UNIFORM: { - assert_invariant(index < Program::UNIFORM_BINDING_COUNT); - auto* currentBo = mContext->uniformState[index].buffer; - if (currentBo) { - currentBo->boundUniformBuffers.unset(index); - } - bo->boundUniformBuffers.set(index); - mContext->uniformState[index] = BufferState { - .buffer = bo, - .offset = offset, - .bound = true - }; - - break; - } - - case BufferObjectBinding::SHADER_STORAGE: { - assert_invariant(index < MAX_SSBO_COUNT); - auto* currentBo = mContext->ssboState[index].buffer; - if (currentBo) { - currentBo->boundSsbos.unset(index); - } - bo->boundSsbos.set(index); - mContext->ssboState[index] = BufferState { - .buffer = bo, - .offset = offset, - .bound = true - }; - - break; - } - } -} - -void MetalDriver::bindSamplers(uint32_t index, Handle sbh) { -} - void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value) { FILAMENT_CHECK_PRECONDITION(isInRenderPass(mContext)) @@ -1985,31 +1893,6 @@ } assert_invariant(!error); - // Bind uniform buffers. - MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil }; - NSUInteger uniformOffsets[Program::UNIFORM_BINDING_COUNT] = { 0 }; - enumerateBoundBuffers(BufferObjectBinding::UNIFORM, - [&uniformsToBind, &uniformOffsets](const BufferState& state, MetalBuffer* buffer, - uint32_t index) { - uniformsToBind[index] = buffer; - uniformOffsets[index] = state.offset; - }); - MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder, - UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::COMPUTE, uniformsToBind, - uniformOffsets, Program::UNIFORM_BINDING_COUNT); - - // Bind SSBOs. - MetalBuffer* ssbosToBind[MAX_SSBO_COUNT] = { nil }; - NSUInteger ssboOffsets[MAX_SSBO_COUNT] = { 0 }; - enumerateBoundBuffers(BufferObjectBinding::SHADER_STORAGE, - [&ssbosToBind, &ssboOffsets](const BufferState& state, MetalBuffer* buffer, - uint32_t index) { - ssbosToBind[index] = buffer; - ssboOffsets[index] = state.offset; - }); - MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder, SSBO_BINDING_START, - MetalBuffer::Stage::COMPUTE, ssbosToBind, ssboOffsets, MAX_SSBO_COUNT); - [computeEncoder setComputePipelineState:computePipelineState]; MTLSize threadgroupsPerGrid = MTLSizeMake(workGroupCount.x, workGroupCount.y, workGroupCount.z); @@ -2067,32 +1950,6 @@ mContext->timerQueryImpl->endTimeElapsedQuery(tq); } -void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType, - const std::function& f) { - assert_invariant(bindingType == BufferObjectBinding::UNIFORM || - bindingType == BufferObjectBinding::SHADER_STORAGE); - - auto enumerate = [&](auto arrayType){ - for (auto i = 0u; i < arrayType.size(); i++) { - const auto& thisBuffer = arrayType[i]; - if (!thisBuffer.bound) { - continue; - } - f(thisBuffer, thisBuffer.buffer->getBuffer(), i); - } - }; - - switch (bindingType) { - default: - case (BufferObjectBinding::UNIFORM): - enumerate(mContext->uniformState); - break; - case (BufferObjectBinding::SHADER_STORAGE): - enumerate(mContext->ssboState); - break; - } -} - void MetalDriver::resetState(int) { } diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 550063ba535..02deea6575c 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -140,12 +140,6 @@ class MetalBufferObject : public HwBufferObject { void updateBufferUnsynchronized(void* data, size_t size, uint32_t byteOffset); MetalBuffer* getBuffer() { return &buffer; } - // Tracks which uniform/ssbo buffers this buffer object is bound into. - static_assert(Program::UNIFORM_BINDING_COUNT <= 32); - static_assert(MAX_SSBO_COUNT <= 32); - utils::bitset32 boundUniformBuffers; - utils::bitset32 boundSsbos; - private: MetalBuffer buffer; }; diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 611b36293a2..8a718288f52 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -99,9 +99,6 @@ void NoopDriver::destroyProgram(Handle ph) { void NoopDriver::destroyRenderTarget(Handle rth) { } -void NoopDriver::destroySamplerGroup(Handle sbh) { -} - void NoopDriver::destroySwapChain(Handle sch) { } @@ -279,11 +276,6 @@ void NoopDriver::setExternalStream(Handle th, Handle sh) { void NoopDriver::generateMipmaps(Handle th) { } -void NoopDriver::updateSamplerGroup(Handle sbh, - BufferDescriptor&& data) { - scheduleDestroy(std::move(data)); -} - void NoopDriver::compilePrograms(CompilerPriorityQueue priority, CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { if (callback) { @@ -306,16 +298,6 @@ void NoopDriver::makeCurrent(Handle drawSch, Handle re void NoopDriver::commit(Handle sch) { } -void NoopDriver::bindUniformBuffer(uint32_t index, Handle ubh) { -} - -void NoopDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index, - Handle ubh, uint32_t offset, uint32_t size) { -} - -void NoopDriver::bindSamplers(uint32_t index, Handle sbh) { -} - void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index e5fbc567ca6..9cb6a5bd354 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -543,11 +543,6 @@ Handle OpenGLDriver::createProgramS() noexcept { return initHandle(); } -Handle OpenGLDriver::createSamplerGroupS() noexcept { - // TODO: goes away - return {}; -} - Handle OpenGLDriver::createTextureS() noexcept { return initHandle(); } @@ -731,11 +726,6 @@ void OpenGLDriver::createProgramR(Handle ph, Program&& program) { CHECK_GL_ERROR(utils::slog.e) } -void OpenGLDriver::createSamplerGroupR(Handle sbh, uint32_t size, - utils::FixedSizeString<32>) { - // TODO: goes away -} - UTILS_NOINLINE void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t, uint32_t width, uint32_t height, uint32_t depth, bool useProtectedMemory) noexcept { @@ -1788,10 +1778,6 @@ void OpenGLDriver::destroyProgram(Handle ph) { } } -void OpenGLDriver::destroySamplerGroup(Handle sbh) { - // TODO: goes away -} - void OpenGLDriver::destroyTexture(Handle th) { DEBUG_MARKER() @@ -2529,10 +2515,6 @@ void OpenGLDriver::resetBufferObject(Handle boh) { } } -void OpenGLDriver::updateSamplerGroup(Handle sbh, BufferDescriptor&& data) { - // TODO: goes away -} - void OpenGLDriver::update3DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, @@ -3151,19 +3133,6 @@ void OpenGLDriver::setScissor(Viewport const& scissor) noexcept { // Setting rendering state // ------------------------------------------------------------------------------------------------ -void OpenGLDriver::bindUniformBuffer(uint32_t index, Handle ubh) { - // TODO: goes away -} - -void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index, - Handle ubh, uint32_t offset, uint32_t size) { - // TODO: goes away -} - -void OpenGLDriver::bindSamplers(uint32_t index, Handle sbh) { - // TODO: goes away -} - void OpenGLDriver::insertEventMarker(char const* string) { #ifndef __EMSCRIPTEN__ #ifdef GL_EXT_debug_marker diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index c48f5047a2d..c8f0e04ead8 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -466,11 +466,6 @@ void VulkanDriver::finish(int dummy) { FVK_SYSTRACE_END(); } -void VulkanDriver::createSamplerGroupR(Handle sbh, uint32_t count, - utils::FixedSizeString<32> debugName) { - PANIC_PRECONDITION("Sampler Group is deprecated"); -} - void VulkanDriver::createRenderPrimitiveR(Handle rph, Handle vbh, Handle ibh, PrimitiveType pt) { @@ -807,10 +802,6 @@ Handle VulkanDriver::importTextureS() noexcept { return mResourceAllocator.allocHandle(); } -Handle VulkanDriver::createSamplerGroupS() noexcept { - return mResourceAllocator.allocHandle(); -} - Handle VulkanDriver::createRenderPrimitiveS() noexcept { return mResourceAllocator.allocHandle(); } @@ -857,23 +848,6 @@ Handle VulkanDriver::createDescriptorSetS() noexcept { return mResourceAllocator.allocHandle(); } -void VulkanDriver::destroySamplerGroup(Handle sbh) { - if (!sbh) { - return; - } - // Unlike most of the other "Hw" handles, the sampler buffer is an abstract concept and does - // not map to any Vulkan objects. To handle destruction, the only thing we need to do is - // ensure that the next draw call doesn't try to access a zombie sampler buffer. Therefore, - // simply replace all weak references with null. - auto* hwsb = mResourceAllocator.handle_cast(sbh); - for (auto& binding : mSamplerBindings) { - if (binding == hwsb) { - binding = nullptr; - } - } - mResourceManager.release(hwsb); -} - void VulkanDriver::destroySwapChain(Handle sch) { if (!sch) { return; @@ -1272,11 +1246,6 @@ void VulkanDriver::generateMipmaps(Handle th) { } while ((srcw > 1 || srch > 1) && level < t->levels); } -void VulkanDriver::updateSamplerGroup(Handle sbh, - BufferDescriptor&& data) { - PANIC_PRECONDITION("Sampler Group is deprecated"); -} - void VulkanDriver::compilePrograms(CompilerPriorityQueue priority, CallbackHandler* handler, CallbackHandler::Callback callback, void* user) { if (callback) { @@ -1606,18 +1575,6 @@ void VulkanDriver::commit(Handle sch) { FVK_SYSTRACE_END(); } -void VulkanDriver::bindUniformBuffer(uint32_t index, Handle boh) { -} - -void VulkanDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index, - Handle boh, uint32_t offset, uint32_t size) { -} - -void VulkanDriver::bindSamplers(uint32_t index, Handle sbh) { - auto* hwsb = mResourceAllocator.handle_cast(sbh); - mSamplerBindings[index] = hwsb; -} - void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value) { assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants"); diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index f939a1eabad..fe5bef83774 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -935,15 +935,6 @@ void RenderPass::Executor::execute(FEngine& engine, constexpr size_t const maxCommandSizeInBytes = sizeof(CustomCommand) + sizeof(COMMAND_TYPE(scissor)) + - sizeof(COMMAND_TYPE(bindUniformBuffer)) + - sizeof(COMMAND_TYPE(bindSamplers)) + - sizeof(COMMAND_TYPE(bindBufferRange)) + - sizeof(COMMAND_TYPE(bindBufferRange)) + - sizeof(COMMAND_TYPE(bindSamplers)) + - sizeof(COMMAND_TYPE(bindSamplers)) + - sizeof(COMMAND_TYPE(bindUniformBuffer)) + - sizeof(COMMAND_TYPE(bindSamplers)) + - sizeof(COMMAND_TYPE(bindSamplers)) + sizeof(COMMAND_TYPE(bindPipeline)) + sizeof(COMMAND_TYPE(setPushConstant)) + sizeof(COMMAND_TYPE(bindRenderPrimitive)) + From c0b1daf3399c1d0580bd9cdce6e6d873ff71c828 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 12:23:45 -0700 Subject: [PATCH 38/49] MetalDescriptorSet move implementation into MetalHandles.mm --- filament/backend/src/metal/MetalHandles.h | 113 +-------------------- filament/backend/src/metal/MetalHandles.mm | 107 +++++++++++++++++++ 2 files changed, 109 insertions(+), 111 deletions(-) diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 02deea6575c..acf7f8bffaa 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -487,118 +487,9 @@ class MetalDescriptorSetLayout : public HwDescriptorSetLayout { struct MetalDescriptorSet : public HwDescriptorSet { MetalDescriptorSet(MetalDescriptorSetLayout* layout) noexcept; - void finalize(MetalDriver* driver) { - [driver->mContext->currentRenderPassEncoder useResource:driver->mContext->emptyBuffer - usage:MTLResourceUsageRead]; - [driver->mContext->currentRenderPassEncoder - useResource:getOrCreateEmptyTexture(driver->mContext) - usage:MTLResourceUsageRead]; - - if (@available(iOS 13.0, *)) { - [driver->mContext->currentRenderPassEncoder useResources:vertexResources.data() - count:vertexResources.size() - usage:MTLResourceUsageRead - stages:MTLRenderStageVertex]; - [driver->mContext->currentRenderPassEncoder useResources:fragmentResources.data() - count:fragmentResources.size() - usage:MTLResourceUsageRead - stages:MTLRenderStageFragment]; - } else { - [driver->mContext->currentRenderPassEncoder useResources:vertexResources.data() - count:vertexResources.size() - usage:MTLResourceUsageRead]; - [driver->mContext->currentRenderPassEncoder useResources:fragmentResources.data() - count:fragmentResources.size() - usage:MTLResourceUsageRead]; - } - } - - id finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage) { - auto const index = static_cast(stage); - assert_invariant(index < 3); - auto& buffer = cachedBuffer[index]; - - if (buffer) { - return buffer; - } + void finalize(MetalDriver* driver); - // Map all the texture bindings to their respective texture types. - auto const& bindings = layout->getBindings(); - auto textureTypes = - utils::FixedCapacityVector::with_capacity(bindings.size()); - for (auto const& binding : bindings) { - if (!hasShaderType(binding.stageFlags, stage)) { - continue; - } - MTLTextureType textureType = MTLTextureType2D; - if (auto found = textures.find(binding.binding); found != textures.end()) { - auto const& textureBinding = textures[binding.binding]; - textureType = textureBinding.texture.textureType; - } - textureTypes.push_back(textureType); - } - - MetalContext const& context = *driver->mContext; - - id encoder = - layout->getArgumentEncoder(context.device, stage, textureTypes); - - buffer = [context.device newBufferWithLength:encoder.encodedLength - options:MTLResourceStorageModeShared]; - [encoder setArgumentBuffer:buffer offset:0]; - - for (auto const& binding : bindings) { - if (!hasShaderType(binding.stageFlags, stage)) { - continue; - } - switch (binding.type) { - case DescriptorType::UNIFORM_BUFFER: - case DescriptorType::SHADER_STORAGE_BUFFER: { - auto found = buffers.find(binding.binding); - if (found == buffers.end()) { - [encoder setBuffer:driver->mContext->emptyBuffer - offset:0 - atIndex:binding.binding * 2]; - continue; - } - - auto const& bufferBinding = buffers[binding.binding]; - [encoder setBuffer:bufferBinding.buffer - offset:bufferBinding.offset - atIndex:binding.binding * 2]; - break; - } - case DescriptorType::SAMPLER: { - auto found = textures.find(binding.binding); - if (found == textures.end()) { - [encoder setTexture:driver->mContext->emptyTexture - atIndex:binding.binding * 2]; - id sampler = - driver->mContext->samplerStateCache.getOrCreateState({}); - [encoder setSamplerState:sampler - atIndex:binding.binding * 2 + 1]; - continue; - } - - auto const& textureBinding = textures[binding.binding]; - [encoder setTexture:textureBinding.texture atIndex:binding.binding * 2]; - SamplerState samplerState { - .samplerParams = textureBinding.sampler - }; - id sampler = - driver->mContext->samplerStateCache.getOrCreateState(samplerState); - [encoder setSamplerState:sampler - atIndex:binding.binding * 2 + 1]; - break; - } - case DescriptorType::INPUT_ATTACHMENT: - assert_invariant(false); - break; - } - } - - return buffer; - } + id finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage); MetalDescriptorSetLayout* layout; diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index c213a115478..65e110ff37b 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -1312,5 +1312,112 @@ static void func(void* user) { MetalDescriptorSet::MetalDescriptorSet(MetalDescriptorSetLayout* layout) noexcept : layout(layout) {} +void MetalDescriptorSet::finalize(MetalDriver* driver) { + [driver->mContext->currentRenderPassEncoder useResource:driver->mContext->emptyBuffer + usage:MTLResourceUsageRead]; + [driver->mContext->currentRenderPassEncoder + useResource:getOrCreateEmptyTexture(driver->mContext) + usage:MTLResourceUsageRead]; + + if (@available(iOS 13.0, *)) { + [driver->mContext->currentRenderPassEncoder useResources:vertexResources.data() + count:vertexResources.size() + usage:MTLResourceUsageRead + stages:MTLRenderStageVertex]; + [driver->mContext->currentRenderPassEncoder useResources:fragmentResources.data() + count:fragmentResources.size() + usage:MTLResourceUsageRead + stages:MTLRenderStageFragment]; + } else { + [driver->mContext->currentRenderPassEncoder useResources:vertexResources.data() + count:vertexResources.size() + usage:MTLResourceUsageRead]; + [driver->mContext->currentRenderPassEncoder useResources:fragmentResources.data() + count:fragmentResources.size() + usage:MTLResourceUsageRead]; + } +} + +id MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage) { + auto const index = static_cast(stage); + assert_invariant(index < 3); + auto& buffer = cachedBuffer[index]; + + if (buffer) { + return buffer; + } + + // Map all the texture bindings to their respective texture types. + auto const& bindings = layout->getBindings(); + auto textureTypes = utils::FixedCapacityVector::with_capacity(bindings.size()); + for (auto const& binding : bindings) { + if (!hasShaderType(binding.stageFlags, stage)) { + continue; + } + MTLTextureType textureType = MTLTextureType2D; + if (auto found = textures.find(binding.binding); found != textures.end()) { + auto const& textureBinding = textures[binding.binding]; + textureType = textureBinding.texture.textureType; + } + textureTypes.push_back(textureType); + } + + MetalContext const& context = *driver->mContext; + + id encoder = + layout->getArgumentEncoder(context.device, stage, textureTypes); + + buffer = [context.device newBufferWithLength:encoder.encodedLength + options:MTLResourceStorageModeShared]; + [encoder setArgumentBuffer:buffer offset:0]; + + for (auto const& binding : bindings) { + if (!hasShaderType(binding.stageFlags, stage)) { + continue; + } + switch (binding.type) { + case DescriptorType::UNIFORM_BUFFER: + case DescriptorType::SHADER_STORAGE_BUFFER: { + auto found = buffers.find(binding.binding); + if (found == buffers.end()) { + [encoder setBuffer:driver->mContext->emptyBuffer + offset:0 + atIndex:binding.binding * 2]; + continue; + } + + auto const& bufferBinding = buffers[binding.binding]; + [encoder setBuffer:bufferBinding.buffer + offset:bufferBinding.offset + atIndex:binding.binding * 2]; + break; + } + case DescriptorType::SAMPLER: { + auto found = textures.find(binding.binding); + if (found == textures.end()) { + [encoder setTexture:driver->mContext->emptyTexture atIndex:binding.binding * 2]; + id sampler = + driver->mContext->samplerStateCache.getOrCreateState({}); + [encoder setSamplerState:sampler atIndex:binding.binding * 2 + 1]; + continue; + } + + auto const& textureBinding = textures[binding.binding]; + [encoder setTexture:textureBinding.texture atIndex:binding.binding * 2]; + SamplerState samplerState { .samplerParams = textureBinding.sampler }; + id sampler = + driver->mContext->samplerStateCache.getOrCreateState(samplerState); + [encoder setSamplerState:sampler atIndex:binding.binding * 2 + 1]; + break; + } + case DescriptorType::INPUT_ATTACHMENT: + assert_invariant(false); + break; + } + } + + return buffer; +} + } // namespace backend } // namespace filament From 8e30b1bfc9969b02c01d6650cf669e3a1e72848d Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 12:26:53 -0700 Subject: [PATCH 39/49] Rename toString --- filament/backend/src/metal/MetalDriver.mm | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 87819d3b183..f5a6f516444 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -599,16 +599,20 @@ // nothing to do, timer query was constructed in createTimerQueryS } -const char* prettyDescriptorType(DescriptorType type) { +const char* toString(DescriptorType type) { switch (type) { - case DescriptorType::UNIFORM_BUFFER: return "UNIFORM_BUFFER"; - case DescriptorType::SHADER_STORAGE_BUFFER: return "SHADER_STORAGE_BUFFER"; - case DescriptorType::SAMPLER: return "SAMPLER"; - case DescriptorType::INPUT_ATTACHMENT: return "INPUT_ATTACHMENT"; + case DescriptorType::UNIFORM_BUFFER: + return "UNIFORM_BUFFER"; + case DescriptorType::SHADER_STORAGE_BUFFER: + return "SHADER_STORAGE_BUFFER"; + case DescriptorType::SAMPLER: + return "SAMPLER"; + case DescriptorType::INPUT_ATTACHMENT: + return "INPUT_ATTACHMENT"; } } -const char* prettyShaderStageFlags(ShaderStageFlags flags) { +const char* toString(ShaderStageFlags flags) { std::vector stages; if (any(flags & ShaderStageFlags::VERTEX)) { stages.push_back("VERTEX"); @@ -633,7 +637,7 @@ return buffer; } -const char* prettyDescriptorFlags(DescriptorFlags flags) { +const char* toString(DescriptorFlags flags) { if (flags == DescriptorFlags::DYNAMIC_OFFSET) { return "DYNAMIC_OFFSET"; } @@ -645,9 +649,8 @@ DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); for (size_t i = 0; i < info.bindings.size(); i++) { DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s}", - info.bindings[i].binding, prettyDescriptorType(info.bindings[i].type), - info.bindings[i].count, prettyShaderStageFlags(info.bindings[i].stageFlags), - prettyDescriptorFlags(info.bindings[i].flags)); + info.bindings[i].binding, toString(info.bindings[i].type), info.bindings[i].count, + toString(info.bindings[i].stageFlags), toString(info.bindings[i].flags)); DEBUG_LOG(",\n"); } DEBUG_LOG("})\n"); From 99b3587dd302d78d62d3bbf8fcd39fd62e58e523 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 12:28:35 -0700 Subject: [PATCH 40/49] Update comment --- filament/backend/src/metal/MetalHandles.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 65e110ff37b..e16d5191d11 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -1265,7 +1265,8 @@ static void func(void* user) { ShaderStage stage, utils::FixedCapacityVector const& textureTypes) { auto const& bindings = getBindings(); NSMutableArray* arguments = [NSMutableArray new]; - // important! the bindings must be sorted by binding number + // Important! The bindings must be sorted by binding number. This has already been done inside + // createDescriptorSetLayout. size_t textureIndex = 0; for (auto const& binding : bindings) { if (!hasShaderType(binding.stageFlags, stage)) { From f4cd9f712a7f2026b4aa49fc21ac75221d136ad2 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 16:51:11 -0700 Subject: [PATCH 41/49] GLSLPostProcessor clean up, remove magic numbers --- filament/backend/src/metal/MetalDriver.mm | 16 +- filament/backend/src/metal/MetalState.h | 6 +- filament/backend/src/metal/MetalState.mm | 7 +- libs/filamat/src/GLSLPostProcessor.cpp | 210 +++++----------------- libs/filamat/src/shaders/CodeGenerator.h | 5 +- 5 files changed, 57 insertions(+), 187 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index f5a6f516444..8f80246c5c7 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1236,8 +1236,10 @@ } // Bind descriptor sets. - mContext->vertexDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); - mContext->fragmentDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + mContext->vertexDescriptorBindings.bindBuffers( + mContext->currentRenderPassEncoder, DESCRIPTOR_SET_BINDING_START); + mContext->fragmentDescriptorBindings.bindBuffers( + mContext->currentRenderPassEncoder, DESCRIPTOR_SET_BINDING_START); for (auto& pc : mContext->currentPushConstants) { pc.clear(); @@ -1810,8 +1812,10 @@ descriptorSet->finalize(this); mContext->finalizedDescriptorSets.insert(descriptorSet); } - mContext->vertexDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); - mContext->fragmentDescriptorBindings.bindBuffers(mContext->currentRenderPassEncoder, 21); + mContext->vertexDescriptorBindings.bindBuffers( + mContext->currentRenderPassEncoder, DESCRIPTOR_SET_BINDING_START); + mContext->fragmentDescriptorBindings.bindBuffers( + mContext->currentRenderPassEncoder, DESCRIPTOR_SET_BINDING_START); } } @@ -1825,10 +1829,10 @@ const auto [size, data] = mContext->dynamicOffsets.getOffsets(); [mContext->currentRenderPassEncoder setFragmentBytes:data length:size * sizeof(uint32_t) - atIndex:25]; + atIndex:DYNAMIC_OFFSET_BINDING]; [mContext->currentRenderPassEncoder setVertexBytes:data length:size * sizeof(uint32_t) - atIndex:25]; + atIndex:DYNAMIC_OFFSET_BINDING]; mContext->dynamicOffsets.setDirty(false); } diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index bb63fbf5d59..99ee96d873a 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -68,10 +68,8 @@ static constexpr uint32_t ZERO_VERTEX_BUFFER_BINDING = 0u; static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u; // These constants must match the equivalent in CodeGenerator.h. -// TODO: deprecate these -static constexpr uint32_t UNIFORM_BUFFER_BINDING_START = 17u; -static constexpr uint32_t SSBO_BINDING_START = 0u; -static constexpr uint32_t SAMPLER_GROUP_BINDING_START = 27u; +static constexpr uint32_t DESCRIPTOR_SET_BINDING_START = 21u; +static constexpr uint32_t DYNAMIC_OFFSET_BINDING = 25u; // Forward declarations necessary here, definitions at end of file. inline bool operator==(const MTLViewport& lhs, const MTLViewport& rhs); diff --git a/filament/backend/src/metal/MetalState.mm b/filament/backend/src/metal/MetalState.mm index 6d42de61850..065e2b62cce 100644 --- a/filament/backend/src/metal/MetalState.mm +++ b/filament/backend/src/metal/MetalState.mm @@ -263,10 +263,9 @@ mDirtyBuffers.reset(); } -// TODO: don't use constant 4 -template class MetalBufferBindings<4, ShaderStage::VERTEX>; -template class MetalBufferBindings<4, ShaderStage::FRAGMENT>; -template class MetalBufferBindings<4, ShaderStage::COMPUTE>; +template class MetalBufferBindings; +template class MetalBufferBindings; +template class MetalBufferBindings; } // namespace backend } // namespace filament diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 77d13ae9622..9f464e824d2 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -58,79 +58,24 @@ namespace msl { // this is only used for MSL using BindingIndexMap = std::unordered_map; -// I want to know: - -// 0. DescriptorSetBindingPoint::PER_VIEW -// which Ubos (there may be more than 1)? ~and which are in use~ (we assume all are in use) -// for each UBO (which needs to be in order): -// what's the name of the pointer -// what's the type of Struct it points to? -// for example: -// frameUniforms, FrameUniforms -// shadowUniforms, ShadowUniforms -// etc -// which Sibs (there is only ever 1) -// what's the name of the sampler -// what's the type of the sampler - -// 1. DescriptorSetBindingPoint::PER_RENDERABLE -// which Ubos (there may be more than 1) and which are in use? -// which Sibs (there is only ever 1) - -// 2. DescriptorSetBindingPoint::PER_MATERIAL -// which Ubos (there may be more than 1) and which are in use? -// which Sibs (there is only ever 1) - -// In order to get the ubos, I can do: -// descriptor_sets = {} // automatically add set key to empty [] array if key/value pair doesn't exist -// for each ubo inside Ubo: -// binding = getBinding(ubo) -// interface_block = get(ubo) -// descriptor_set[binding.set].push_back((binding.binding, interface_block)) -// for each descriptor_set inside descriptor_sets: -// sort the descriptor_set by binding -// for each (binding, interface_block) in descriptor_set: -// type_name = interface_block.getName() # e.g., FrameUniforms -// entry_name = type_name.first_letter_lowercase() - -// When Filament goes to create an arg buffer, will it create one that only contains the uniforms in use? - -//static void collectSibs(const GLSLPostProcessor::Config& config, SibVector& sibs) { -// switch (config.domain) { -// case MaterialDomain::SURFACE: -// UTILS_NOUNROLL -// for (uint8_t blockIndex = 0; blockIndex < CONFIG_SAMPLER_BINDING_COUNT; blockIndex++) { -// if (blockIndex == SamplerBindingPoints::PER_MATERIAL_INSTANCE) { -// continue; -// } -// auto const* sib = -// SibGenerator::getSib((SamplerBindingPoints)blockIndex, config.variant); -// if (sib && hasShaderType(sib->getStageFlags(), config.shaderType)) { -// sibs.emplace_back(blockIndex, sib); -// } -// } -// case MaterialDomain::POST_PROCESS: -// case MaterialDomain::COMPUTE: -// break; -// } -// sibs.emplace_back((uint8_t) SamplerBindingPoints::PER_MATERIAL_INSTANCE, -// &config.materialInfo->sib); -//} - #ifndef DEBUG_LOG_DESCRIPTOR_SETS #define DEBUG_LOG_DESCRIPTOR_SETS 0 #endif -const char* prettyDescriptorType(DescriptorType type) { +const char* toString(DescriptorType type) { switch (type) { - case DescriptorType::UNIFORM_BUFFER: return "UNIFORM_BUFFER"; - case DescriptorType::SHADER_STORAGE_BUFFER: return "SHADER_STORAGE_BUFFER"; - case DescriptorType::SAMPLER: return "SAMPLER"; - case DescriptorType::INPUT_ATTACHMENT: return "INPUT_ATTACHMENT"; + case DescriptorType::UNIFORM_BUFFER: + return "UNIFORM_BUFFER"; + case DescriptorType::SHADER_STORAGE_BUFFER: + return "SHADER_STORAGE_BUFFER"; + case DescriptorType::SAMPLER: + return "SAMPLER"; + case DescriptorType::INPUT_ATTACHMENT: + return "INPUT_ATTACHMENT"; } } -const char* prettyShaderStageFlags(ShaderStageFlags flags) { +const char* toString(ShaderStageFlags flags) { std::vector stages; if (any(flags & ShaderStageFlags::VERTEX)) { stages.push_back("VERTEX"); @@ -164,12 +109,18 @@ const char* prettyDescriptorFlags(DescriptorFlags flags) { const char* prettyPrintSamplerType(SamplerType type) { switch (type) { - case SamplerType::SAMPLER_2D: return "SAMPLER_2D"; - case SamplerType::SAMPLER_2D_ARRAY: return "SAMPLER_2D_ARRAY"; - case SamplerType::SAMPLER_CUBEMAP: return "SAMPLER_CUBEMAP"; - case SamplerType::SAMPLER_EXTERNAL: return "SAMPLER_EXTERNAL"; - case SamplerType::SAMPLER_3D: return "SAMPLER_3D"; - case SamplerType::SAMPLER_CUBEMAP_ARRAY: return "SAMPLER_CUBEMAP_ARRAY"; + case SamplerType::SAMPLER_2D: + return "SAMPLER_2D"; + case SamplerType::SAMPLER_2D_ARRAY: + return "SAMPLER_2D_ARRAY"; + case SamplerType::SAMPLER_CUBEMAP: + return "SAMPLER_CUBEMAP"; + case SamplerType::SAMPLER_EXTERNAL: + return "SAMPLER_EXTERNAL"; + case SamplerType::SAMPLER_3D: + return "SAMPLER_3D"; + case SamplerType::SAMPLER_CUBEMAP_ARRAY: + return "SAMPLER_CUBEMAP_ARRAY"; } } @@ -268,6 +219,10 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set, descriptors.emplace_back(name, info.bindings[i], std::nullopt); } } + + std::sort(descriptors.begin(), descriptors.end(), [](const auto& a, const auto& b) { + return std::get<1>(a).binding < std::get<1>(b).binding; + }); } void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) { @@ -292,15 +247,14 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) { assert_invariant(sampler.has_value()); printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = " "%s, samplerType = %s}", - name.c_str_safe(), info.binding, prettyDescriptorType(info.type), - info.count, prettyShaderStageFlags(info.stageFlags), - prettyDescriptorFlags(info.flags), prettyPrintSamplerType(sampler->type)); + name.c_str_safe(), info.binding, toString(info.type), info.count, + toString(info.stageFlags), prettyDescriptorFlags(info.flags), + prettyPrintSamplerType(sampler->type)); } else { printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = " "%s}", - name.c_str_safe(), info.binding, prettyDescriptorType(info.type), - info.count, prettyShaderStageFlags(info.stageFlags), - prettyDescriptorFlags(info.flags)); + name.c_str_safe(), info.binding, toString(info.type), info.count, + toString(info.stageFlags), prettyDescriptorFlags(info.flags)); } printf(",\n"); } @@ -308,8 +262,7 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) { } } -// TODO: maybe rename to collectDescriptorSets -static void collectDescriptors(const GLSLPostProcessor::Config& config, DescriptorSets& sets) { +static void collectDescriptorSets(const GLSLPostProcessor::Config& config, DescriptorSets& sets) { auto perViewDescriptors = DescriptorSetInfo::with_capacity(MAX_DESCRIPTOR_COUNT); collectDescriptorsForSet(DescriptorSetBindingPoints::PER_VIEW, config, perViewDescriptors); sets[+DescriptorSetBindingPoints::PER_VIEW] = std::move(perViewDescriptors); @@ -424,88 +377,6 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, auto executionModel = mslCompiler.get_execution_model(); - // Metal Descriptor Sets - // Descriptor set Name Binding - // ---------------------------------------------------------------------- - // 0 Uniforms Individual bindings - // 1-4 Sampler groups [[buffer(27-30)]] - // 5-7 Unused - // - // Here we enumerate each sampler in each sampler group and map it to a Metal resource. Each - // sampler group is its own descriptor set, and each descriptor set becomes an argument buffer. - // - // For example, in GLSL, we might have the following: - // layout( set = 1, binding = 0 ) uniform sampler2D textureA; - // layout( set = 1, binding = 1 ) uniform sampler2D textureB; - // - // This becomes the following MSL argument buffer: - // struct spvDescriptorSetBuffer1 { - // texture2d textureA [[id(0)]]; - // sampler textureASmplr [[id(1)]]; - // texture2d textureB [[id(2)]]; - // sampler textureBSmplr [[id(3)]]; - // }; - // - // Which is then bound to the vertex/fragment functions: - // constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(27)]] -// for (auto [bindingPoint, sib] : sibs) { -// const auto& infoList = sib->getSamplerInfoList(); -// -// // bindingPoint + 1, because the first descriptor set is for uniforms -// auto argBufferBuilder = MetalArgumentBuffer::Builder() -// .name("spvDescriptorSetBuffer" + std::to_string(int(bindingPoint + 1))); -// -// for (const auto& info: infoList) { -// const std::string name = info.uniformName.c_str(); -// argBufferBuilder -// .texture(info.offset * 2, name, info.type, info.format, info.multisample) -// .sampler(info.offset * 2 + 1, name + "Smplr"); -// } -// -// argumentBuffers.push_back(argBufferBuilder.build()); -// -// // This MSLResourceBinding is how we control the [[buffer(n)]] binding of the argument -// // buffer itself; -// MSLResourceBinding argBufferBinding; -// // the baseType doesn't matter, but can't be UNKNOWN -// argBufferBinding.basetype = SPIRType::BaseType::Float; -// argBufferBinding.stage = executionModel; -// argBufferBinding.desc_set = bindingPoint + 1; -// argBufferBinding.binding = kArgumentBufferBinding; -// argBufferBinding.count = 1; -// argBufferBinding.msl_buffer = -// CodeGenerator::METAL_SAMPLER_GROUP_BINDING_START + bindingPoint; -// mslCompiler.add_msl_resource_binding(argBufferBinding); -// } -// -// auto updateResourceBindingDefault = [executionModel, &mslCompiler](const auto& resource) { -// auto set = mslCompiler.get_decoration(resource.id, spv::DecorationDescriptorSet); -// auto binding = mslCompiler.get_decoration(resource.id, spv::DecorationBinding); -// MSLResourceBinding newBinding; -// newBinding.basetype = SPIRType::BaseType::Void; -// newBinding.stage = executionModel; -// newBinding.desc_set = set; -// newBinding.binding = binding; -// newBinding.count = 1; -// newBinding.msl_texture = -// newBinding.msl_sampler = -// newBinding.msl_buffer = binding; -// mslCompiler.add_msl_resource_binding(newBinding); -// }; -// -// auto uniformResources = mslCompiler.get_shader_resources(); -// for (const auto& resource : uniformResources.uniform_buffers) { -// updateResourceBindingDefault(resource); -// } -// auto ssboResources = mslCompiler.get_shader_resources(); -// for (const auto& resource : ssboResources.storage_buffers) { -// updateResourceBindingDefault(resource); -// } - - // Descriptor set 0 is uniforms. The add_discrete_descriptor_set call here prevents the uniforms - // from becoming argument buffers. -// mslCompiler.add_discrete_descriptor_set(0); - // Map each descriptor set (argument buffer) to a [[buffer(n)]] binding. // For example, mapDescriptorSet(0, 21) says "map descriptor set 0 to [[buffer(21)]]" auto mapDescriptorSet = [&mslCompiler](uint32_t set, uint32_t buffer) { @@ -518,10 +389,9 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, argBufferBinding.msl_buffer = buffer; mslCompiler.add_msl_resource_binding(argBufferBinding); }; - mapDescriptorSet(0, 21); - mapDescriptorSet(1, 22); - mapDescriptorSet(2, 23); - mapDescriptorSet(3, 24); + for (int i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { + mapDescriptorSet(i, CodeGenerator::METAL_DESCRIPTOR_SET_BINDING_START + i); + } auto resources = mslCompiler.get_shader_resources(); @@ -576,8 +446,8 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, argBufferBuilder .buffer(info.binding * 2 + 0, name.c_str(), lowercasedName); if (any(info.flags & DescriptorFlags::DYNAMIC_OFFSET)) { - // TODO: this requires that the sets and descriptors are sorted (at least - // the uniforms) + // Note: this requires that the sets and descriptors are sorted (at least + // the uniforms). mslCompiler.add_dynamic_buffer( setIndex, info.binding * 2 + 0, dynamicOffsetsBufferIndex++); } @@ -710,7 +580,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co if (internalConfig.mslOutput) { auto sibs = SibVector::with_capacity(CONFIG_SAMPLER_BINDING_COUNT); DescriptorSets descriptors {}; - msl::collectDescriptors(config, descriptors); + msl::collectDescriptorSets(config, descriptors); #if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); #endif @@ -802,7 +672,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, if (internalConfig.mslOutput) { DescriptorSets descriptors {}; - msl::collectDescriptors(config, descriptors); + msl::collectDescriptorSets(config, descriptors); #if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); #endif @@ -846,7 +716,7 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader, if (internalConfig.mslOutput) { DescriptorSets descriptors {}; - msl::collectDescriptors(config, descriptors); + msl::collectDescriptorSets(config, descriptors); #if DEBUG_LOG_DESCRIPTOR_SETS == 1 msl::prettyPrintDescriptorSetInfoVector(descriptors); #endif diff --git a/libs/filamat/src/shaders/CodeGenerator.h b/libs/filamat/src/shaders/CodeGenerator.h index 60105563cbc..47686d70e96 100644 --- a/libs/filamat/src/shaders/CodeGenerator.h +++ b/libs/filamat/src/shaders/CodeGenerator.h @@ -185,9 +185,8 @@ class UTILS_PRIVATE CodeGenerator { // These constants must match the equivalent in MetalState.h. // These values represent the starting index for uniform, ssbo, and sampler group [[buffer(n)]] // bindings. See the chart at the top of MetalState.h. - static constexpr uint32_t METAL_UNIFORM_BUFFER_BINDING_START = 17u; - static constexpr uint32_t METAL_SAMPLER_GROUP_BINDING_START = 27u; - static constexpr uint32_t METAL_SSBO_BINDING_START = 0; + static constexpr uint32_t METAL_DESCRIPTOR_SET_BINDING_START = 21u; + static constexpr uint32_t METAL_DYNAMIC_OFFSET_BINDING = 25u; uint32_t getUniqueSamplerBindingPoint() const noexcept { return mUniqueSamplerBindingPoint++; From e3cb67045b4e038b42d2c052316ec061942c8e0e Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 9 Sep 2024 17:01:53 -0700 Subject: [PATCH 42/49] Remove some more magic numbers --- filament/backend/src/metal/MetalContext.mm | 1 - filament/backend/src/metal/MetalHandles.h | 7 ++++--- filament/backend/src/metal/MetalHandles.mm | 10 +++++----- filament/backend/src/metal/MetalState.h | 2 ++ libs/filamat/src/GLSLPostProcessor.cpp | 2 +- libs/filamat/src/shaders/CodeGenerator.h | 1 + 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/filament/backend/src/metal/MetalContext.mm b/filament/backend/src/metal/MetalContext.mm index e2348388f3b..465ce293a37 100644 --- a/filament/backend/src/metal/MetalContext.mm +++ b/filament/backend/src/metal/MetalContext.mm @@ -173,7 +173,6 @@ bool isInRenderPass(MetalContext* context) { void MetalPushConstantBuffer::setBytes(id encoder, ShaderStage stage) { constexpr size_t PUSH_CONSTANT_SIZE_BYTES = 4; - constexpr size_t PUSH_CONSTANT_BUFFER_INDEX = 20; static char buffer[MAX_PUSH_CONSTANT_COUNT * PUSH_CONSTANT_SIZE_BYTES]; assert_invariant(mPushConstants.size() <= MAX_PUSH_CONSTANT_COUNT); diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index acf7f8bffaa..00e313dd3ae 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -480,8 +480,9 @@ class MetalDescriptorSetLayout : public HwDescriptorSetLayout { DescriptorSetLayout mLayout; size_t mDynamicOffsetCount = 0; - std::array, 3> mCachedArgumentEncoder = { nil }; - std::array, 3> mCachedTextureTypes; + std::array, Program::SHADER_TYPE_COUNT> mCachedArgumentEncoder = { nil }; + std::array, Program::SHADER_TYPE_COUNT> + mCachedTextureTypes; }; struct MetalDescriptorSet : public HwDescriptorSet { @@ -510,7 +511,7 @@ struct MetalDescriptorSet : public HwDescriptorSet { std::vector> externalImages; - std::array, 3> cachedBuffer = { nil }; + std::array, Program::SHADER_TYPE_COUNT> cachedBuffer = { nil }; }; } // namespace backend diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index e16d5191d11..e28a66fe1eb 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -1249,8 +1249,8 @@ static void func(void* user) { id MetalDescriptorSetLayout::getArgumentEncoder(id device, ShaderStage stage, utils::FixedCapacityVector const& textureTypes) { - auto const index = static_cast(stage); - assert_invariant(index < 3); + auto const index = static_cast(stage); + assert_invariant(index < mCachedArgumentEncoder.size()); if (mCachedArgumentEncoder[index] && std::equal( textureTypes.begin(), textureTypes.end(), mCachedTextureTypes[index].begin())) { @@ -1302,7 +1302,7 @@ static void func(void* user) { break; } case DescriptorType::INPUT_ATTACHMENT: - // TODO: what to do here? + // TODO: support INPUT_ATTACHMENT assert_invariant(false); break; } @@ -1340,8 +1340,8 @@ static void func(void* user) { } id MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage) { - auto const index = static_cast(stage); - assert_invariant(index < 3); + auto const index = static_cast(stage); + assert_invariant(index < cachedBuffer.size()); auto& buffer = cachedBuffer[index]; if (buffer) { diff --git a/filament/backend/src/metal/MetalState.h b/filament/backend/src/metal/MetalState.h index 99ee96d873a..1e14cddc258 100644 --- a/filament/backend/src/metal/MetalState.h +++ b/filament/backend/src/metal/MetalState.h @@ -67,7 +67,9 @@ static constexpr uint32_t ZERO_VERTEX_BUFFER_BINDING = 0u; static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u; + // These constants must match the equivalent in CodeGenerator.h. +static constexpr uint32_t PUSH_CONSTANT_BUFFER_INDEX = 20u; static constexpr uint32_t DESCRIPTOR_SET_BINDING_START = 21u; static constexpr uint32_t DYNAMIC_OFFSET_BINDING = 25u; diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 9f464e824d2..aaca4ac5ff9 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -476,7 +476,7 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl, pushConstantBinding.desc_set = kPushConstDescSet; pushConstantBinding.binding = kPushConstBinding; pushConstantBinding.count = 1; - pushConstantBinding.msl_buffer = 20; + pushConstantBinding.msl_buffer = CodeGenerator::METAL_PUSH_CONSTANT_BUFFER_INDEX; mslCompiler.add_msl_resource_binding(pushConstantBinding); *outMsl = mslCompiler.compile(); diff --git a/libs/filamat/src/shaders/CodeGenerator.h b/libs/filamat/src/shaders/CodeGenerator.h index 47686d70e96..dac8553c444 100644 --- a/libs/filamat/src/shaders/CodeGenerator.h +++ b/libs/filamat/src/shaders/CodeGenerator.h @@ -185,6 +185,7 @@ class UTILS_PRIVATE CodeGenerator { // These constants must match the equivalent in MetalState.h. // These values represent the starting index for uniform, ssbo, and sampler group [[buffer(n)]] // bindings. See the chart at the top of MetalState.h. + static constexpr uint32_t METAL_PUSH_CONSTANT_BUFFER_INDEX = 20u; static constexpr uint32_t METAL_DESCRIPTOR_SET_BINDING_START = 21u; static constexpr uint32_t METAL_DYNAMIC_OFFSET_BINDING = 25u; From 22e62b92a6632cd2a8a4cdb5ae06a0720c2cb718 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 9 Sep 2024 23:09:01 -0700 Subject: [PATCH 43/49] disable compute test to fix build this test is no important for now, since we don't have a finished compute api. the test breaks because it still use the old non-descriptor based API. --- filament/backend/CMakeLists.txt | 36 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index 078953915d3..be00e2b07b3 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -500,24 +500,24 @@ endif() # ================================================================================================== # Compute tests - -if (NOT IOS AND NOT WEBGL) - -add_executable(compute_test - test/ComputeTest.cpp - test/Arguments.cpp - test/test_ComputeBasic.cpp - ) - -target_link_libraries(compute_test PRIVATE - backend - getopt - gtest - ) - -set_target_properties(compute_test PROPERTIES FOLDER Tests) - -endif() +# +#if (NOT IOS AND NOT WEBGL) +# +#add_executable(compute_test +# test/ComputeTest.cpp +# test/Arguments.cpp +# test/test_ComputeBasic.cpp +# ) +# +#target_link_libraries(compute_test PRIVATE +# backend +# getopt +# gtest +# ) +# +#set_target_properties(compute_test PROPERTIES FOLDER Tests) +# +#endif() # ================================================================================================== # Metal utils tests From 1596a5ec7117610255b0f168c3ee1087ad6645ce Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 10 Sep 2024 11:29:57 -0700 Subject: [PATCH 44/49] vk: Fix descriptor set bitmask padding The sizing of each field in Bitmask has changed, and the description was no longer accurate. This caused msan to fail because the mask is also used as a key to cache. --- filament/backend/src/vulkan/VulkanHandles.cpp | 6 +++--- filament/backend/src/vulkan/VulkanHandles.h | 12 +++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index c42b3d048c8..87da8ed7daa 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -136,9 +136,9 @@ inline VkDescriptorSetLayoutCreateInfo getLayoutCreateInfo(DescriptorSetLayout c return dlinfo; } -using Bitmask = VulkanDescriptorSetLayout::Bitmask; -Bitmask fromBackendLayout(DescriptorSetLayout const& layout) { - Bitmask mask; +using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask; +BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) { + BitmaskGroup mask; for (auto const& binding: layout.bindings) { switch (binding.type) { case DescriptorType::UNIFORM_BUFFER: { diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 4bdc6b91e61..e566e09159e 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -49,20 +49,18 @@ struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout // The bitmask representation of a set layout. struct Bitmask { - UniformBufferBitmask ubo = 0; // 4 bytes - UniformBufferBitmask dynamicUbo = 0; // 4 bytes + // TODO: better utiltize the space below and use bitset instead. + UniformBufferBitmask ubo = 0; // 8 bytes + UniformBufferBitmask dynamicUbo = 0; // 8 bytes SamplerBitmask sampler = 0; // 8 bytes - InputAttachmentBitmask inputAttachment = 0; // 1 bytes - - // Because we're using this struct as hash key, must make it's 8-bytes aligned, with no - // unaccounted bytes. - uint8_t padding[7] = {}; // 7 bytes + InputAttachmentBitmask inputAttachment = 0; // 8 bytes bool operator==(Bitmask const& right) const { return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler && inputAttachment == right.inputAttachment; } }; + static_assert(sizeof(Bitmask) == 32); // This is a convenience struct to quickly check layout compatibility in terms of descriptor set // pools. From 37d1baf0e8417e8c911ee56c5a44a14a84581dff Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 10 Sep 2024 13:36:00 -0700 Subject: [PATCH 45/49] Metal debug logging improvements --- filament/backend/src/metal/MetalDriver.mm | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 8f80246c5c7..3e76effd826 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -646,6 +646,8 @@ void MetalDriver::createDescriptorSetLayoutR( Handle dslh, DescriptorSetLayout&& info) { + std::sort(info.bindings.begin(), info.bindings.end(), + [](const auto& a, const auto& b) { return a.binding < b.binding; }); DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId()); for (size_t i = 0; i < info.bindings.size(); i++) { DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s}", @@ -654,8 +656,6 @@ DEBUG_LOG(",\n"); } DEBUG_LOG("})\n"); - std::sort(info.bindings.begin(), info.bindings.end(), - [](const auto& a, const auto& b) { return a.binding < b.binding; }); construct_handle(dslh, std::move(info)); } @@ -788,6 +788,7 @@ } void MetalDriver::destroyTexture(Handle th) { + DEBUG_LOG("destroyTexture(th = %d)\n", th.getId()); if (!th) { return; } @@ -1785,16 +1786,18 @@ auto descriptorSet = handle_cast(dsh); const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount(); utils::FixedCapacityVector offsetsVector(dynamicBindings, 0); +#if FILAMENT_METAL_DEBUG_LOG == 1 DEBUG_LOG("bindDescriptorSet(dsh = %d, set = %d, offsets = [", dsh.getId(), set); for (size_t i = 0; i < dynamicBindings; i++) { - DEBUG_LOG("%d", offsets[i]); + printf("%d", offsets[i]); if (i < dynamicBindings - 1) { - DEBUG_LOG(", "); + printf(", "); } offsetsVector[i] = offsets[i]; } DEBUG_LOG("])\n"); +#endif // Bind the descriptor set. mContext->currentDescriptorSets[set] = descriptorSet; From 6397fa3fe1ac51e4e42476dcc4fb1a5300e98d72 Mon Sep 17 00:00:00 2001 From: Sungun Park Date: Wed, 11 Sep 2024 18:51:15 -0700 Subject: [PATCH 46/49] Fix a crash for color grading (#8118) Set postprocess descriptor set for color grading --- filament/src/PostProcessManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 680ae506569..c3597364659 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -2284,6 +2284,9 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver, // the UBO has been set and committed in colorGradingPrepareSubpass() FMaterialInstance* mi = material.getMaterialInstance(mEngine); mi->use(driver); + + bindPostProcessDescriptorSet(driver); + const Variant::type_t variant = Variant::type_t(colorGradingConfig.translucent ? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE); From f37d8b2fc26f6ef717d9d72a0b19afb961b588fa Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 11 Sep 2024 14:30:08 -0700 Subject: [PATCH 47/49] vk: refactor descriptor bitmask to use bitset Using bitset enables simpler counting of bits and the use of the handy 'forEachSetBit'. Also cleaned up various unused code. --- filament/backend/src/vulkan/VulkanDriver.cpp | 2 +- filament/backend/src/vulkan/VulkanDriver.h | 2 +- filament/backend/src/vulkan/VulkanHandles.cpp | 26 ++-- filament/backend/src/vulkan/VulkanHandles.h | 71 ++++------ filament/backend/src/vulkan/VulkanUtility.h | 89 ++---------- .../caching/VulkanDescriptorSetManager.cpp | 128 +++++++----------- .../caching/VulkanDescriptorSetManager.h | 5 +- 7 files changed, 101 insertions(+), 222 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index c8f0e04ead8..731a3cc127d 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1839,7 +1839,7 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) { mBoundPipeline = { .program = program, .pipelineLayout = pipelineLayout, - .descriptorSetMask = descriptorSetMaskTable[layoutCount], + .descriptorSetMask = DescriptorSetMask(descriptorSetMaskTable[layoutCount]), }; mPipelineCache.bindLayout(pipelineLayout); diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h index 84609f41d89..0f454855e8a 100644 --- a/filament/backend/src/vulkan/VulkanDriver.h +++ b/filament/backend/src/vulkan/VulkanDriver.h @@ -163,7 +163,7 @@ class VulkanDriver final : public DriverBase { struct BoundPipeline { VulkanProgram* program; VkPipelineLayout pipelineLayout; - uint8_t descriptorSetMask; + DescriptorSetMask descriptorSetMask; }; BoundPipeline mBoundPipeline = {}; RenderPassFboBundle mRenderPassFboInfo; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 87da8ed7daa..acd8c6dcd05 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -52,15 +52,14 @@ void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeight) { } template -static constexpr Bitmask fromStageFlags(ShaderStageFlags flags, uint8_t binding) { - Bitmask ret = 0; - if ((flags & ShaderStageFlags::VERTEX) != ShaderStageFlags::NONE) { - ret |= (getVertexStage() << binding); +inline void fromStageFlags(backend::ShaderStageFlags stage, descriptor_binding_t binding, + Bitmask& mask) { + if ((bool) (stage & ShaderStageFlags::VERTEX)) { + mask.set(binding + getVertexStageShift()); } - if ((flags & ShaderStageFlags::FRAGMENT) != ShaderStageFlags::NONE) { - ret |= (getFragmentStage() << binding); + if ((bool) (stage & ShaderStageFlags::FRAGMENT)) { + mask.set(binding + getFragmentStageShift()); } - return ret; } inline VkDescriptorSetLayout createDescriptorSetLayout(VkDevice device, @@ -143,21 +142,18 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) { switch (binding.type) { case DescriptorType::UNIFORM_BUFFER: { if ((binding.flags & DescriptorFlags::DYNAMIC_OFFSET) != DescriptorFlags::NONE) { - mask.dynamicUbo |= fromStageFlags(binding.stageFlags, - binding.binding); + fromStageFlags(binding.stageFlags, binding.binding, mask.dynamicUbo); } else { - mask.ubo |= fromStageFlags(binding.stageFlags, - binding.binding); + fromStageFlags(binding.stageFlags, binding.binding, mask.ubo); } break; } case DescriptorType::SAMPLER: { - mask.sampler |= fromStageFlags(binding.stageFlags, binding.binding); + fromStageFlags(binding.stageFlags, binding.binding, mask.sampler); break; } case DescriptorType::INPUT_ATTACHMENT: { - mask.inputAttachment |= - fromStageFlags(binding.stageFlags, binding.binding); + fromStageFlags(binding.stageFlags, binding.binding, mask.inputAttachment); break; } case DescriptorType::SHADER_STORAGE_BUFFER: @@ -174,7 +170,7 @@ VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(VkDevice device, DescriptorSetLayout const& layout) : VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT), mDevice(device), vklayout(createDescriptorSetLayout(device, getLayoutCreateInfo(layout))), - bitmask(fromBackendLayout(layout)), bindings(getBindings(bitmask)), + bitmask(fromBackendLayout(layout)), count(Count::fromLayoutBitmask(bitmask)) { } diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index e566e09159e..d44c0964c81 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -29,13 +29,27 @@ #include #include +#include #include #include #include namespace filament::backend { -using namespace descset; +namespace { +// Counts the total number of descriptors for both vertex and fragment stages. +template +inline uint8_t collapsedCount(Bitmask const& mask) { + static_assert(sizeof(mask) <= 64); + constexpr uint64_t VERTEX_MASK = (1ULL << getVertexStageShift()) - 1ULL; + constexpr uint64_t FRAGMENT_MASK = (1ULL << getFragmentStageShift()) - 1ULL; + uint64_t val = mask.getValue(); + val = ((val | VERTEX_MASK) >> getVertexStageShift()) | + ((val | FRAGMENT_MASK) >> getFragmentStageShift()); + return (uint8_t) Bitmask(val).count(); +} + +} // anonymous namespace class VulkanTimestamps; struct VulkanBufferObject; @@ -50,10 +64,10 @@ struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout // The bitmask representation of a set layout. struct Bitmask { // TODO: better utiltize the space below and use bitset instead. - UniformBufferBitmask ubo = 0; // 8 bytes - UniformBufferBitmask dynamicUbo = 0; // 8 bytes - SamplerBitmask sampler = 0; // 8 bytes - InputAttachmentBitmask inputAttachment = 0; // 8 bytes + UniformBufferBitmask ubo; // 8 bytes + UniformBufferBitmask dynamicUbo; // 8 bytes + SamplerBitmask sampler; // 8 bytes + InputAttachmentBitmask inputAttachment; // 8 bytes bool operator==(Bitmask const& right) const { return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler && @@ -81,10 +95,10 @@ struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout static inline Count fromLayoutBitmask(Bitmask const& mask) { return { - .ubo = countBits(collapseStages(mask.ubo)), - .dynamicUbo = countBits(collapseStages(mask.dynamicUbo)), - .sampler = countBits(collapseStages(mask.sampler)), - .inputAttachment = countBits(collapseStages(mask.inputAttachment)), + .ubo = collapsedCount(mask.ubo), + .dynamicUbo = collapsedCount(mask.dynamicUbo), + .sampler = collapsedCount(mask.sampler), + .inputAttachment = collapsedCount(mask.inputAttachment), }; } @@ -100,8 +114,6 @@ struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout } }; - static_assert(sizeof(Bitmask) % 8 == 0); - VulkanDescriptorSetLayout(VkDevice device, DescriptorSetLayout const& layout); ~VulkanDescriptorSetLayout(); @@ -109,44 +121,7 @@ struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout VkDevice const mDevice; VkDescriptorSetLayout const vklayout; Bitmask const bitmask; - - // This is a convenience struct so that we don't have to iterate through all the bits of the - // bitmask (which correspondings to binding indices). - struct _Bindings { - utils::FixedCapacityVector const ubo; - utils::FixedCapacityVector const dynamicUbo; - utils::FixedCapacityVector const sampler; - utils::FixedCapacityVector const inputAttachment; - } bindings; - Count const count; - -private: - - template - utils::FixedCapacityVector bits(MaskType mask) { - utils::FixedCapacityVector ret = - utils::FixedCapacityVector::with_capacity(countBits(mask)); - for (uint8_t i = 0; i < sizeof(mask) * 4; ++i) { - if (mask & (1 << i)) { - ret.push_back(i); - } - } - return ret; - } - - _Bindings getBindings(Bitmask const& bitmask) { - auto const uboCollapsed = collapseStages(bitmask.ubo); - auto const dynamicUboCollapsed = collapseStages(bitmask.dynamicUbo); - auto const samplerCollapsed = collapseStages(bitmask.sampler); - auto const inputAttachmentCollapsed = collapseStages(bitmask.inputAttachment); - return { - bits(uboCollapsed), - bits(dynamicUboCollapsed), - bits(samplerCollapsed), - bits(inputAttachmentCollapsed), - }; - } }; struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet { diff --git a/filament/backend/src/vulkan/VulkanUtility.h b/filament/backend/src/vulkan/VulkanUtility.h index 43ba73b6db4..33be80d3fa8 100644 --- a/filament/backend/src/vulkan/VulkanUtility.h +++ b/filament/backend/src/vulkan/VulkanUtility.h @@ -19,6 +19,7 @@ #include +#include #include #include @@ -527,94 +528,28 @@ class CappedArray { uint32_t mInd = 0; }; -// TODO: ok to remove once Filament-side API is complete -namespace descset { - -using UniformBufferBitmask = uint64_t; -using SamplerBitmask = uint64_t; +using UniformBufferBitmask = utils::bitset64; +using SamplerBitmask = utils::bitset64; // We only have at most one input attachment, so this bitmask exists only to make the code more // general. -using InputAttachmentBitmask = uint64_t; - -constexpr UniformBufferBitmask UBO_VERTEX_STAGE = 0x1; -constexpr UniformBufferBitmask UBO_FRAGMENT_STAGE = (0x1ULL << (sizeof(UniformBufferBitmask) * 4)); -constexpr SamplerBitmask SAMPLER_VERTEX_STAGE = 0x1; -constexpr SamplerBitmask SAMPLER_FRAGMENT_STAGE = (0x1ULL << (sizeof(SamplerBitmask) * 4)); -constexpr InputAttachmentBitmask INPUT_ATTACHMENT_VERTEX_STAGE = 0x1; -constexpr InputAttachmentBitmask INPUT_ATTACHMENT_FRAGMENT_STAGE = - (0x1ULL << (sizeof(InputAttachmentBitmask) * 4)); - +using InputAttachmentBitmask = utils::bitset64; template -static constexpr Bitmask getVertexStage() noexcept { - if constexpr (std::is_same_v) { - return UBO_VERTEX_STAGE; - } - if constexpr (std::is_same_v) { - return SAMPLER_VERTEX_STAGE; - } - if constexpr (std::is_same_v) { - return INPUT_ATTACHMENT_VERTEX_STAGE; - } +static constexpr uint8_t getVertexStageShift() noexcept { + // We assume the bottom half of bits are for vertex stages. + return 0; } template -static constexpr Bitmask getFragmentStage() noexcept { - if constexpr (std::is_same_v) { - return UBO_FRAGMENT_STAGE; - } - if constexpr (std::is_same_v) { - return SAMPLER_FRAGMENT_STAGE; - } - if constexpr (std::is_same_v) { - return INPUT_ATTACHMENT_FRAGMENT_STAGE; - } +static constexpr uint8_t getFragmentStageShift() noexcept { + // We assume the top half of bits are for fragment stages. + return sizeof(Bitmask) * 4; } -} // namespace descset - -namespace { -// Use constexpr to statically generate a bit count table for 8-bit numbers. -struct _BitCountHelper { - constexpr _BitCountHelper() : data{} { - for (uint16_t i = 0; i < 256; ++i) { - data[i] = 0; - for (auto j = i; j > 0; j /= 2) { - if (j & 1) { - data[i]++; - } - } - } - } - - template - constexpr uint8_t count(MaskType num) { - uint8_t count = 0; - for (uint8_t i = 0; i < sizeof(MaskType) * 8; i+=8) { - count += data[(num >> i) & 0xFF]; - } - return count; - } - -private: - uint8_t data[256]; -}; -} // namespace anonymous +// We have at most 4 descriptor sets. This is to indicate which ones are active. +using DescriptorSetMask = utils::bitset8; -template -inline uint8_t countBits(MaskType num) { - static _BitCountHelper BitCounter = {}; - return BitCounter.count(num); -} - -// This is useful for counting the total number of descriptors for both vertex and fragment stages. -template -inline MaskType collapseStages(MaskType mask) { - constexpr uint8_t NBITS_DIV_2 = sizeof(MaskType) * 4; - // First zero out the top-half and then or the bottom-half against the original top-half. - return ((mask << NBITS_DIV_2) >> NBITS_DIV_2) | (mask >> NBITS_DIV_2); -} } // namespace filament::backend diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp index 0b0a51d5667..596b37ee3fd 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp @@ -34,12 +34,12 @@ namespace filament::backend { namespace { -using Bitmask = VulkanDescriptorSetLayout::Bitmask; +using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask; using DescriptorCount = VulkanDescriptorSetLayout::Count; using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray; -using BitmaskHashFn = utils::hash::MurmurHashFn; -struct BitmaskEqual { - bool operator()(Bitmask const& k1, Bitmask const& k2) const { +using BitmaskGroupHashFn = utils::hash::MurmurHashFn; +struct BitmaskGroupEqual { + bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; } }; @@ -154,7 +154,7 @@ class DescriptorPool { return vkSet; } - void recycle(Bitmask const& layoutMask, VkDescriptorSet vkSet) { + void recycle(BitmaskGroup const& layoutMask, VkDescriptorSet vkSet) { // We are recycling - release the set back into the pool. Note that the // vk handle has not changed, but we need to change the backend handle to allow // for proper refcounting of resources referenced in this set. @@ -178,8 +178,8 @@ class DescriptorPool { uint16_t mUnusedCount; // This maps a layout ot a list of descriptor sets allocated for that layout. - using UnusedSetMap = - std::unordered_map, BitmaskHashFn, BitmaskEqual>; + using UnusedSetMap = std::unordered_map, + BitmaskGroupHashFn, BitmaskGroupEqual>; UnusedSetMap mUnused; }; @@ -224,7 +224,8 @@ class DescriptorInfinitePool { return ret; } - void recycle(DescriptorCount const& count, Bitmask const& layoutMask, VkDescriptorSet vkSet) { + void recycle(DescriptorCount const& count, BitmaskGroup const& layoutMask, + VkDescriptorSet vkSet) { for (auto& pool: mPools) { if (!pool->canAllocate(count)) { continue; @@ -258,33 +259,20 @@ class VulkanDescriptorSetManager::Impl { public: DescriptorSetHistory() : mResources(nullptr) {} - DescriptorSetHistory(Bitmask const& mask, DescriptorCount const& count, - VkDescriptorSetLayout layout, VulkanResourceAllocator* allocator, + DescriptorSetHistory(BitmaskGroup const& mask, VulkanResourceAllocator* allocator, VulkanDescriptorSet* set) - : mResources(allocator), + : dynamicUboMask(mask.dynamicUbo), + mResources(allocator), mSet(set), - mMask(mask), - mLayout(layout), - mCount(count), - mMaxIndex(0), - mWritten(0), mBound(false) { - assert_invariant(mCount.total() < sizeof(mWritten) * 8); // initial state is unbound. + mResources.acquire(mSet); unbind(); - - for (uint8_t i = 0; i < 32; ++i) { - if ((mMask.ubo | mMask.dynamicUbo | mMask.sampler | mMask.inputAttachment) & - (1LL < i)) { - mMaxIndex = i; - } - } } ~DescriptorSetHistory() { - if (mSet) { - mResources.clear(); - } + assert_invariant(mSet); + mResources.clear(); } void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept { @@ -293,20 +281,14 @@ class VulkanDescriptorSetManager::Impl { } void write(uint8_t binding) noexcept { - mWritten |= (1LL << binding); mBound = false; } - void write(uint8_t binding, VkImageSubresourceRange const& range, VulkanTexture* texture) noexcept { - write(binding); - mTextures.insert({texture, range}); - } - // Ownership will be transfered to the commandbuffer. void bind(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, uint8_t index) noexcept { VkCommandBuffer const cmdbuffer = commands->buffer(); - vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index, 1, - &mSet->vkSet, mCount.dynamicUbo, mOffsets.data()); + vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, + index, 1, &mSet->vkSet, (uint32_t) dynamicUboMask.count(), mOffsets.data()); commands->acquire(mSet); mResources.clear(); @@ -319,41 +301,33 @@ class VulkanDescriptorSetManager::Impl { } bool bound() const noexcept { return mBound; } - bool written(uint8_t binding) const noexcept { return mWritten & (1LL << binding); } - VkDescriptorSetLayout layout() const noexcept { return mLayout; } - Bitmask const& mask() const noexcept { return mMask; } - uint8_t maxIndex() const noexcept { return mMaxIndex; } + + UniformBufferBitmask const dynamicUboMask; private: FixedSizeVulkanResourceManager<1> mResources; VulkanDescriptorSet* mSet = nullptr; - CappedArray mTextures; - backend::DescriptorSetOffsetArray mOffsets; - Bitmask mMask = {}; - VkDescriptorSetLayout mLayout = VK_NULL_HANDLE; - DescriptorCount mCount = {}; - uint8_t mMaxIndex = 0; - uint64_t mWritten = 0; bool mBound = false; }; struct BoundInfo { VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; - uint8_t setMask = 0; + DescriptorSetMask setMask; DescriptorSetArray boundSets = {}; bool operator==(BoundInfo const& info) const { if (pipelineLayout != info.pipelineLayout || setMask != info.setMask) { return false; } - for (uint8_t i = 0; i < UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { - if ((bool) (setMask & (1LL << i)) && boundSets[i] != info.boundSets[i]) { - return false; + bool equal = true; + setMask.forEachSetBit([&](size_t i) { + if (boundSets[i] != info.boundSets[i]) { + equal = false; } - } - return true; + }); + return equal; } }; @@ -378,36 +352,36 @@ class VulkanDescriptorSetManager::Impl { mStashedSets[setIndex] = set->vkSet; } - void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, uint8_t setMask) { - using iter_type = decltype(mHistory)::iterator; - std::array iterators = {mHistory.end()}; - bool allBound = true; - - for (uint8_t i = 0; i < UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { - if ((setMask & (1LL << i)) == 0) { - continue; - } - auto const vkset = mStashedSets[i]; - if (auto itr = mHistory.find(vkset); itr != mHistory.end() && !itr->second.bound()) { - allBound = false; - iterators[i] = itr; + void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, + DescriptorSetMask const& setMask) { + std::array updateSets = {nullptr}; + auto const& historyEnd = mHistory.end(); + + // setMask indicates the set of descriptor sets the driver wants to bind, curMask is the + // actual set of sets that *needs* to be bound. + DescriptorSetMask curMask = setMask; + + setMask.forEachSetBit([&](size_t index) { + auto const vkset = mStashedSets[index]; + if (auto itr = mHistory.find(vkset); itr != historyEnd && !itr->second.bound()) { + updateSets[index] = &itr->second; + } else { + curMask.unset(index); } - } + }); + BoundInfo nextInfo = { pipelineLayout, setMask, mStashedSets, }; - if (allBound && mLastBoundInfo == nextInfo) { + if (curMask.none() && mLastBoundInfo == nextInfo) { return; } - for (uint8_t i = 0; i < UNIQUE_DESCRIPTOR_SET_COUNT; ++i) { - auto& itr = iterators[i]; - if (itr != mHistory.end()) { - itr->second.bind(commands, pipelineLayout, i); - } - } + curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) { + updateSets[index]->bind(commands, pipelineLayout, index); + }); mLastBoundInfo = nextInfo; } @@ -422,7 +396,7 @@ class VulkanDescriptorSetManager::Impl { VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; auto& history = mHistory[set->vkSet]; - if (history.mask().dynamicUbo & (1LL << binding)) { + if (history.dynamicUboMask.test(binding)) { type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; } VkWriteDescriptorSet const descriptorWrite = { @@ -468,7 +442,7 @@ class VulkanDescriptorSetManager::Impl { }; vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr); set->acquire(texture); - mHistory[set->vkSet].write(binding, range, texture); + mHistory[set->vkSet].write(binding); } void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept { @@ -501,7 +475,7 @@ class VulkanDescriptorSetManager::Impl { mHistory.emplace( std::piecewise_construct, std::forward_as_tuple(vkSet), - std::forward_as_tuple(layout->bitmask, layout->count, layout->vklayout, mResourceAllocator, set)); + std::forward_as_tuple(layout->bitmask, mResourceAllocator, set)); } void destroySet(Handle handle) { @@ -565,7 +539,7 @@ void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set } void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands, - VkPipelineLayout pipelineLayout, uint8_t setMask) { + VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) { mImpl->commit(commands, pipelineLayout, setMask); } diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h index 3d06ff2c8f6..c35ca3293ef 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h @@ -32,8 +32,6 @@ namespace filament::backend { -using namespace descset; - // [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to // introduce descriptor set. This PR will arrive before that change is complete. As such, some of // the methods introduced here will be obsolete, and certain logic will be generalized. @@ -60,7 +58,8 @@ class VulkanDescriptorSetManager { void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets); - void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, uint8_t setMask); + void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, + DescriptorSetMask const& setMask); void setPlaceHolders(VkSampler sampler, VulkanTexture* texture, VulkanBufferObject* bufferObject) noexcept; From c01d2c09b6a9c3f79d5b999e1bf3a9824dd19fa9 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 11 Sep 2024 14:56:12 -0700 Subject: [PATCH 48/49] vk: reduce map look-up in descriptor set hot loop DescriptorSetManager's commit() is the inner most of the rendering loop. We want to avoid any hash map look up here. We just store a pointer to the history object where appropriate. --- .../caching/VulkanDescriptorSetManager.cpp | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp index 596b37ee3fd..1fac7200c14 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp @@ -251,8 +251,6 @@ struct Equal { class VulkanDescriptorSetManager::Impl { private: - using DescriptorSetArray = std::array; - struct DescriptorSetHistory { private: using TextureBundle = std::pair; @@ -312,10 +310,13 @@ class VulkanDescriptorSetManager::Impl { bool mBound = false; }; + using DescriptorSetHistoryArray = + std::array; + struct BoundInfo { VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; DescriptorSetMask setMask; - DescriptorSetArray boundSets = {}; + DescriptorSetHistoryArray boundSets; bool operator==(BoundInfo const& info) const { if (pipelineLayout != info.pipelineLayout || setMask != info.setMask) { @@ -344,28 +345,23 @@ class VulkanDescriptorSetManager::Impl { auto& history = mHistory[set->vkSet]; history.setOffsets(std::move(offsets)); - auto const lastSet = mStashedSets[setIndex]; - if (lastSet != VK_NULL_HANDLE) { - assert_invariant(mHistory.find(lastSet) != mHistory.end()); - mHistory[lastSet].unbind(); + auto lastHistory = mStashedSets[setIndex]; + if (lastHistory) { + lastHistory->unbind(); } - mStashedSets[setIndex] = set->vkSet; + mStashedSets[setIndex] = &history; } void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) { - std::array updateSets = {nullptr}; - auto const& historyEnd = mHistory.end(); + DescriptorSetHistoryArray& updateSets = mStashedSets; // setMask indicates the set of descriptor sets the driver wants to bind, curMask is the // actual set of sets that *needs* to be bound. DescriptorSetMask curMask = setMask; setMask.forEachSetBit([&](size_t index) { - auto const vkset = mStashedSets[index]; - if (auto itr = mHistory.find(vkset); itr != historyEnd && !itr->second.bound()) { - updateSets[index] = &itr->second; - } else { + if (!updateSets[index] || updateSets[index]->bound()) { curMask.unset(index); } }); @@ -373,7 +369,7 @@ class VulkanDescriptorSetManager::Impl { BoundInfo nextInfo = { pipelineLayout, setMask, - mStashedSets, + updateSets, }; if (curMask.none() && mLastBoundInfo == nextInfo) { return; @@ -480,10 +476,12 @@ class VulkanDescriptorSetManager::Impl { void destroySet(Handle handle) { VulkanDescriptorSet* set = mResourceAllocator->handle_cast(handle); + DescriptorSetHistory* history = &mHistory[set->vkSet]; mHistory.erase(set->vkSet); + for (uint8_t i = 0; i < mStashedSets.size(); ++i) { - if (mStashedSets[i] == set->vkSet) { - mStashedSets[i] = VK_NULL_HANDLE; + if (mStashedSets[i] == history) { + mStashedSets[i] = nullptr; } } } @@ -495,7 +493,7 @@ class VulkanDescriptorSetManager::Impl { DescriptorInfinitePool mDescriptorPool; std::pair mInputAttachment; std::unordered_map mHistory; - DescriptorSetArray mStashedSets = { VK_NULL_HANDLE }; + DescriptorSetHistoryArray mStashedSets; BoundInfo mLastBoundInfo; From 69d5ac2107a08c560a8d78d7bc3e817e4b9555b7 Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Tue, 10 Sep 2024 13:33:35 -0700 Subject: [PATCH 49/49] Check for invalid texture inside MaterialInstance --- filament/src/details/MaterialInstance.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index 04c5fb0519f..3428f4d2188 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -190,6 +190,10 @@ void FMaterialInstance::commit(DriverApi& driver) const { if (!mTextureParameters.empty()) { for (auto const& [binding, p]: mTextureParameters) { assert_invariant(p.texture); + // TODO: figure out a way to do this more efficiently (isValid() is a hashmap lookup) + FEngine& engine = mMaterial->getEngine(); + FILAMENT_CHECK_PRECONDITION(engine.isValid(p.texture)) + << "Invalid texture still bound to MaterialInstance: '" << getName() << "'\n"; Handle handle = p.texture->getHwHandleForSampling(); assert_invariant(handle); mDescriptorSet.setSampler(binding, handle, p.params);