From f11a04722bd92ff5ba1f85381c00b711ceb9800e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 7 Jun 2024 10:10:45 -0700 Subject: [PATCH] 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 | 130 +++---- .../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 | 191 ++++------ 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 | 247 ++++++------ filament/src/MaterialParser.h | 67 ++-- filament/src/PostProcessManager.cpp | 57 ++- filament/src/PostProcessManager.h | 40 +- filament/src/RenderPass.cpp | 275 +++++++------- filament/src/RenderPass.h | 79 ++-- 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 | 101 ++--- 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 | 27 +- 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 | 72 ++-- 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 | 162 +++++--- 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, 4875 insertions(+), 2470 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 db0255d414..fcec79f686 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 dfce1af4a1..d2cd70daaa 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 class RenderableManager { 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 796acedb7d..96d1b57a19 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 6a7f66cc2f..79d8ce967b 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 0000000000..3c7b85664b --- /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 d69a9991d2..b01adc7ad0 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 c54e9609ce..2cf5224414 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 @@ private: // 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 220d04bbf2..051a381a52 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 7cec72cd0c..f8e9d50eb8 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 @@ public: 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 @@ public: 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; + // Descriptor binding (set, binding, type -> shader name) info + Program& descriptorBindings(backend::descriptor_set_t set, + DescriptorBindingsInfo descriptorBindings) 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 - }; - - Program& specializationConstants( - utils::FixedCapacityVector specConstants) noexcept; + Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept; struct PushConstant { utils::CString name; @@ -129,33 +114,40 @@ public: Program& multiview(bool multiview) noexcept; + // For ES2 support only... + Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept; + Program& attributes(AttributesInfo attributes) noexcept; + + // + // Getters for program construction... + // + ShaderSource const& getShadersSource() const noexcept { return mShadersSource; } ShaderSource& getShadersSource() noexcept { return mShadersSource; } - UniformBlockInfo const& getUniformBlockBindings() const noexcept { return mUniformBlocks; } - UniformBlockInfo& getUniformBlockBindings() noexcept { return mUniformBlocks; } - - SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; } - SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; } - - auto const& getBindingUniformInfo() const { return mBindingUniformInfo; } - auto& getBindingUniformInfo() { return mBindingUniformInfo; } - - auto const& getAttributes() const { return mAttributes; } - auto& getAttributes() { return mAttributes; } - 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 @@ public: return mPushConstants[static_cast(stage)]; } - uint64_t getCacheId() const noexcept { return mCacheId; } + auto const& getBindingUniformInfo() const { return mBindingUniformsInfo; } + auto& getBindingUniformInfo() { return mBindingUniformsInfo; } - bool isMultiview() const noexcept { return mMultiview; } - - 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{}; - utils::Invocable mLogger; - utils::FixedCapacityVector mSpecializationConstants; - std::array, SHADER_TYPE_COUNT> mPushConstants; - utils::FixedCapacityVector> mAttributes; - std::array mBindingUniformInfo; CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH; + utils::Invocable mLogger; + SpecializationConstantsInfo mSpecializationConstants; + std::array, SHADER_TYPE_COUNT> mPushConstants; + 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 527052378e..1fe1644681 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 b92a07f37b..9575b1b8e7 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 68d997a439..dfd1a964d9 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 afc6dc924e..e5eaf6c1a8 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 24acfe60e1..c7c1a20415 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 53bfa5a7a1..bb4249f82a 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 58852a6714..caabe35834 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -289,6 +289,21 @@ void MetalDriver::endFrame(uint32_t frameId) { #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 @@ void MetalDriver::createTimerQueryR(Handle tqh, int) { // 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 @@ Handle MetalDriver::createTimerQueryS() noexcept { 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::destroyTimerQuery(Handle tqh) { } } +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::bindBufferRange(BufferObjectBinding bindingType, uint32_t inde } } -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 @@ void MetalDriver::bindPipeline(PipelineState const& ps) { 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 @@ void MetalDriver::bindPipeline(PipelineState const& ps) { 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 @@ void MetalDriver::bindRenderPrimitive(Handle rph) { 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 4d6f864639..4f7c0eb7b0 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -200,12 +200,12 @@ public: 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 3d56956387..991c7ed03d 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -482,7 +482,7 @@ MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept // 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 83579cb5d5..1473373037 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 ea5c526402..ba7d6b92d5 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 0000000000..b4f53d1b5d --- /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 0000000000..8669236ca2 --- /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 0000000000..72d42fec33 --- /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 0000000000..bce3519fce --- /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 902fcc9f09..8659ba3d2a 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -60,10 +60,19 @@ public: 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 @@ public: // 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 @@ public: 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 @@ private: 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 414d0c8aa2..83253c7f40 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()); + // 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 d5e1b797b6..927ae925a5 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 @@ public: } 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: // 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 @@ private: 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 6c5eab9a44..42943075ee 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); - } - CHECK_GL_ERROR(utils::slog.e) - } - } - } else + 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) + } + } + + 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 19be485ac6..59cccf57f6 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 @@ public: 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 @@ private: 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 @@ private: 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 22e4e1814d..0057b89d31 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 ff3f137c10..3cd2ff38fc 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 75596ea93c..52032a16a1 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -482,6 +482,12 @@ private: 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 5bb694c974..6eede04f00 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 777a96f040..981b0eaa07 100644 --- a/filament/backend/test/BackendTest.h +++ b/filament/backend/test/BackendTest.h @@ -51,10 +51,15 @@ protected: 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 61c8b89e5e..599ac9e71e 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 bde771448a..e93b088116 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)); - auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0); + DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({ + {{ + DescriptorType::UNIFORM_BUFFER, + ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0, + DescriptorFlags::NONE, 0 + }}}); + + DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout); + + 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 7062259559..0e5febfa56 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 7babb40745..c30d660454 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 2af57eb719..8e7e3df7f3 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 } }); + .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - auto sgroup = api.createSamplerGroup(samplers.getSize(), utils::FixedSizeString<32>("Test")); - api.updateSamplerGroup(sgroup, samplers.toBufferDescriptor(api)); + api.bindDescriptorSet(descriptorSet, 0, {}); - api.bindSamplers(0, sgroup); - - 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 3c54839dad..4ced788c0d 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 b8261434b2..3c8a7dfee8 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); - 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(); } diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 363ef2d7c1..9badecf682 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -489,33 +489,11 @@ public: * 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 @@ public: 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 @@ public: 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 0304253b90..8beb83c5e5 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,52 +631,94 @@ 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)) { - return false; - } - assert_invariant(count == utils::Enum::count()); - - UTILS_NOUNROLL - for (size_t i = 0; i < count; i++) { - if (!unflattener.read(&samplerGroupBindingInfoList[i].bindingOffset)) { - return false; - } - if (!unflattener.read((uint8_t *)&samplerGroupBindingInfoList[i].shaderStageFlags)) { - return false; - } - if (!unflattener.read(&samplerGroupBindingInfoList[i].count)) { - return false; - } - } - - if (!unflattener.read(&count)) { + uint8_t setCount; + if (!unflattener.read(&setCount)) { return false; } - samplerBindingToNameMap.reserve(count); - samplerBindingToNameMap.resize(count); - for (size_t i = 0; i < count; i++) { - uint8_t binding; - if (!unflattener.read(&binding)) { + 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; } - assert_invariant(binding < backend::MAX_SAMPLER_COUNT); - if (!unflattener.read(&samplerBindingToNameMap[binding])) { + + uint8_t descriptorCount; + if (!unflattener.read(&descriptorCount)) { 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)}); + } } return true; } +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; + } + 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; +} + bool ChunkMaterialConstants::unflatten(filaflat::Unflattener& unflattener, utils::FixedCapacityVector* materialConstants) { assert_invariant(materialConstants); diff --git a/filament/src/MaterialParser.h b/filament/src/MaterialParser.h index b10a549d8b..544fdcf810 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 @@ public: 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 @@ public: } 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 @@ private: 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 @@ private: 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 ChunkSamplerBlockBindings { +struct ChunkDescriptorBindingsInfo { static bool unflatten(filaflat::Unflattener& unflattener, - SamplerGroupBindingInfoList* pSamplerGroupBindingInfoList, - SamplerBindingToNameMap* pSamplerBindingToNameMap); + MaterialParser::DescriptorBindingsContainer* container); + using Container = MaterialParser::DescriptorBindingsContainer; + static filamat::ChunkType const tag = filamat::MaterialDescriptorBindingsInfo; +}; + +struct ChunkDescriptorSetLayoutInfo { + static bool unflatten(filaflat::Unflattener& unflattener, + 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 85e21d6832..44a4231604 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); - uniforms.commit(driver); + mSsrPassDescriptorSet.commit(mEngine); + + 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 76f5bcd325..7bed969cc9 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 ee5092370d..ba463fc662 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 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. - // create a temporary UBO for instancing - size_t const count = mCommandEnd - mCommandBegin; + // 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(!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(!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 e1c7b3c181..f748c305b3 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 @@ public: 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::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 + 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 + 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 + uint32_t rfu[2]; // 16 bytes }; static_assert(sizeof(PrimitiveInfo) == 56); @@ -321,9 +322,19 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: backend::Viewport const& scissor) noexcept; public: + // fixme: needed in ShadowMapManager Executor() noexcept; // can't be copied @@ -382,7 +394,7 @@ public: } Executor getExecutor(Command const* b, Command const* e) const { - return { this, b, e, mInstancedUboHandle }; + return { *this, b, e }; } private: @@ -394,8 +406,7 @@ private: // 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 @@ private: 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 @@ private: 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 @@ public: } // 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 @@ public: 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 8bd5e5ad57..42918c0b42 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 07d7431f02..a4716399e1 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -41,7 +41,7 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 9550197acf..9c98af9d49 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 2e252633c4..68e6e4729a 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 7015e14466..60cde28b80 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 d0ca26945f..0d6cbc3427 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 @@ public: 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 @@ public: 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 @@ private: { 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 8943371855..e48b520386 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 38017f5c8b..8dee58de14 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 @@ private: 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 596cd8e7fe..c22e728efc 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); - if (builder->mBoneIndicesAndWeightsCount > 0) { - FSkinningBuffer::setIndicesAndWeightsData(downcast(engine), texture, - builder->mBoneIndicesAndWeights, builder->mBoneIndicesAndWeightsCount); - } Bones& bones = manager[ci].bones; - bones.handleSamplerGroup = sampler; - bones.handleTexture = texture; + bones.handleTexture = FSkinningBuffer::createIndicesAndWeightsHandle( + engine, builder->mBoneIndicesAndWeightsCount); + if (builder->mBoneIndicesAndWeightsCount > 0) { + FSkinningBuffer::setIndicesAndWeightsData(engine, + bones.handleTexture, + builder->mBoneIndicesAndWeights, + builder->mBoneIndicesAndWeightsCount); + } // 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 }; + Slice& primitives = mManager[instance].primitives; + if (primitiveIndex < primitives.size()) { + primitives[primitiveIndex].setMorphingBufferOffset(offset); } } } -void FRenderableManager::setMorphTargetBufferAt(Instance instance, uint8_t level, - size_t primitiveIndex, size_t offset, size_t count) { +MorphTargetBuffer* FRenderableManager::getMorphTargetBuffer(Instance instance) const noexcept { if (instance) { - Slice& morphTargets = getMorphTargets(instance, level); - if (primitiveIndex < morphTargets.size()) { - setMorphTargetBufferAt(instance, level, - primitiveIndex, morphTargets[primitiveIndex].buffer, offset, count); - } - } -} - -MorphTargetBuffer* FRenderableManager::getMorphTargetBufferAt(Instance instance, uint8_t level, - size_t primitiveIndex) 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 0fa44c83e7..eebb171ef9 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: 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 2547b40103..7af0b6e22b 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 b8cdb49877..880fe630a2 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 @@ public: 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 @@ private: 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 b8fa8d2999..268af1ded0 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 617f969950..5ef8f709ae 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: 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 @@ private: 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 @@ private: // 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 @@ private: 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 4595c7e6c3..a9ae242d4a 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 fc1d9a35f0..985555b42e 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 namespace filament { @@ -48,27 +58,15 @@ public: 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 @@ public: 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 @@ private: // 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 @@ private: }; 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 d4124fb2a6..71aa13ab7e 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 50e8fc92b9..6e42dcace8 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 @@ public: 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 15094a0759..125f28d5af 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 9690e6386e..03898570e2 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 @@ public: 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 @@ public: 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 b709263867..5970d56c94 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 8fa056a579..fd831ef122 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 @@ public: 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 @@ private: 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 ed9daeb4b0..f3d12686ab 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); + if (mHasShadowing) { + assert_invariant(mShadowMapManager); + mColorPassDescriptorSet.prepareShadowMapping( + mShadowMapManager->getShadowUniformsHandle(), highPrecision); + } } -void FView::cleanupRenderPasses() const noexcept { - mPerViewUniforms.unbindSamplers(); -} - -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 c2fb5bb58b..9a8037f875 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 @@ public: 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 @@ public: 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 @@ public: } backend::Handle getRenderTargetHandle() const noexcept { - backend::Handle kEmptyHandle; + backend::Handle const kEmptyHandle; return mRenderTarget == nullptr ? kEmptyHandle : mRenderTarget->getHwHandle(); } @@ -409,8 +420,7 @@ public: 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 @@ public: return mFogEntity; } - backend::Handle getRenderableUBO() const noexcept { - return mRenderableUbh; + TypedUniformBuffer& getFrameUniforms() noexcept { + return mUniforms; } private: @@ -453,7 +463,7 @@ private: // 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 @@ private: // 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 @@ private: 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 eaf8df8cb4..326db28563 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 { - mSamplerGroupHandle = driver.createSamplerGroup( - mSamplers.getSize(), utils::FixedSizeString<32>("Per-view samplers")); + uint8_t index = 0; - mUniformBufferHandle = driver.createBufferObject(mUniforms.getSize(), - BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC); + 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; +} + + +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 PerViewUniforms::terminate(DriverApi& driver) { - driver.destroyBufferObject(mUniformBufferHandle); - driver.destroySamplerGroup(mSamplerGroupHandle); +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::prepareCamera(FEngine& engine, const CameraInfo& camera) noexcept { +void ColorPassDescriptorSet::terminate(DriverApi& driver) { + for (auto&& entry : mDescriptorSet) { + entry.terminate(driver); + } + for (auto&& entry : mDescriptorSetLayout) { + entry.terminate(driver); + } +} + +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 c8df066190..3c3bc04df2 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 @@ public: 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 @@ public: 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 0000000000..722224c578 --- /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 0000000000..6cc861efd9 --- /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 0000000000..646a8f5d6c --- /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 0000000000..44c2b00ba1 --- /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 0000000000..3dc0baf312 --- /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 0000000000..edc1b58405 --- /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 458b15db83..ae6aacaaed 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 05fcdd82e5..c1f55c5c89 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 @@ public: 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 @@ public: 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 0000000000..693b7815af --- /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 0000000000..161c4ff651 --- /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 264784dbf6..ea55b7c43d 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 @@ private: } // 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 0000000000..44d2699566 --- /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 ffcd1ce236..6840ce536e 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 4a4561c14a..5ad99cceb6 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 c15c6c457c..9178f6d5e3 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 0000000000..287702f7d0 --- /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 4a8c042e75..7fa90c22d8 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 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) +// 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 +}; + +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 {}; +struct utils::EnableIntegerOperators : public std::true_type {}; +template<> +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 e8f3164eda..0000000000 --- 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 03f5b5e82c..243f2dcfb6 100644 --- a/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h +++ b/libs/filabridge/include/private/filament/SamplerInterfaceBlock.h @@ -49,17 +49,20 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 0000000000..f0a138bc5c --- /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 43ad966ea4..1d1f4c387f 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 #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 2fc20d7e91..c74ad631fa 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 3a4a4824e0..c1e2a7092d 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 11d539870a..b6b7404e6d 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 b2770a2072..0000000000 --- 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 d76078138d..0000000000 --- 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 5f92ca7356..23913f2718 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 5faee0147a..cbb3a85446 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,37 +110,9 @@ private: // ------------------------------------------------------------------------------------------------ -class MaterialUniformBlockBindingsChunk final : public Chunk { - using Container = utils::FixedCapacityVector< - std::pair>; -public: - explicit MaterialUniformBlockBindingsChunk(Container list); - ~MaterialUniformBlockBindingsChunk() final = default; - -private: - void flatten(Flattener&) final; - - Container mBindingList; -}; - -// ------------------------------------------------------------------------------------------------ - -class MaterialSamplerBlockBindingChunk final : public Chunk { -public: - explicit MaterialSamplerBlockBindingChunk(filament::SamplerBindingMap const& samplerBindings); - ~MaterialSamplerBlockBindingChunk() final = default; - -private: - void flatten(Flattener &) final; - - filament::SamplerBindingMap const& mSamplerBindings; -}; - -// ------------------------------------------------------------------------------------------------ - class MaterialBindingUniformInfoChunk final : public Chunk { - using Container = FixedCapacityVector< - std::pair>; + using Container = FixedCapacityVector>; public: explicit MaterialBindingUniformInfoChunk(Container list) noexcept; ~MaterialBindingUniformInfoChunk() final = default; @@ -159,6 +137,38 @@ private: Container mAttributeInfo; }; +// ------------------------------------------------------------------------------------------------ + +class MaterialDescriptorBindingsChuck final : public Chunk { + using Container = filament::SamplerInterfaceBlock; +public: + explicit MaterialDescriptorBindingsChuck(Container const& sib, + filament::backend::DescriptorSetLayout const& perViewLayout) noexcept; + ~MaterialDescriptorBindingsChuck() final = default; + +private: + void flatten(Flattener&) final; + + Container const& mSamplerInterfaceBlock; + filament::backend::DescriptorSetLayout mPerViewLayout; +}; + +// ------------------------------------------------------------------------------------------------ + +class MaterialDescriptorSetLayoutChunk final : public Chunk { + using Container = filament::SamplerInterfaceBlock; +public: + explicit MaterialDescriptorSetLayoutChunk(Container const& sib, + filament::backend::DescriptorSetLayout const& perViewLayout) noexcept; + ~MaterialDescriptorSetLayoutChunk() final = default; + +private: + void flatten(Flattener&) final; + + Container const& mSamplerInterfaceBlock; + filament::backend::DescriptorSetLayout mPerViewLayout; +}; + } // namespace filamat #endif // TNT_FILAMAT_MAT_INTEFFACE_BLOCK_CHUNK_H diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 75d2b940d8..e8a7b3bfcd 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 233243f348..424371d975 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 @@ public: // 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 @@ public: // 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 @@ public: // 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 @@ public: 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 @@ private: 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 4979bed5c3..4324d1a24f 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 6ad24c0148..ccedf738bd 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -16,21 +16,30 @@ #include "ShaderGenerator.h" -#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 +#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 1b419f3b06..55afe0dfa0 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 @@ public: 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 b03a6f3035..ec07d9b186 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") + .name("sampler1") .stageFlags(backend::ShaderStageFlags::VERTEX) - .add({ { "positions", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH }, - { "tangents", Type::SAMPLER_2D_ARRAY, Format::INT, 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::getPerRenderPrimitiveBonesSib(Variant variant) noexcept { - using Type = SamplerInterfaceBlock::Type; - using Format = SamplerInterfaceBlock::Format; - using Precision = SamplerInterfaceBlock::Precision; - - static SamplerInterfaceBlock sib = SamplerInterfaceBlock::Builder() - .name("BonesBuffer") - .stageFlags(backend::ShaderStageFlags::VERTEX) - .add({{"indicesAndWeights", 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 eb874653c5..b510d02d30 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 3736144027..9d4d360b1a 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 a39c44c123..a2250bdad9 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 @@ public: 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 31bbc169f7..2b5af9a9e5 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 6ee6a9d059..45360cfadd 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 9b077e9435..6676de3146 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 43011e25c5..6aeed1ea3b 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 7cb4fb3230..769379278c 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 44b7d99e22..239edbaae0 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 fca5bb0ca1..baa189a1b8 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 1221e7cc32..e45916aed3 100644 --- a/libs/utils/include/utils/FixedCapacityVector.h +++ b/libs/utils/include/utils/FixedCapacityVector.h @@ -84,7 +84,7 @@ public: 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 adf64a80c8..0465dc1cb1 100644 --- a/libs/utils/src/EntityManagerImpl.h +++ b/libs/utils/src/EntityManagerImpl.h @@ -68,9 +68,6 @@ public: 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 @@ public: 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 6bd418cf43..3b590e62c6 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 8ae6d3f966..e56bfc6c3c 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 32f6421986..54acfd0f8b 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 17029480ca..33d99eb803 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 024ec9ff41..09569c6155 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 b6d902d744..9ef63c79e8 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 5084b10b64..0d12391078 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 0db8a4ebf2..fa55b05728 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 ce6be8f7ba..b2f9d3cc72 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 1bc74372c1..c0d6bdb61f 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 33ea12cd80..a3ff0059c8 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 9a3eb63302..50f44e26c1 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;