diff --git a/filament/backend/src/vulkan/VulkanAsyncHandles.cpp b/filament/backend/src/vulkan/VulkanAsyncHandles.cpp index 83d00e1f3f86..231209cbafc5 100644 --- a/filament/backend/src/vulkan/VulkanAsyncHandles.cpp +++ b/filament/backend/src/vulkan/VulkanAsyncHandles.cpp @@ -16,6 +16,10 @@ #include "VulkanAsyncHandles.h" +#include "VulkanConstants.h" + +#include "vulkan/utils/Spirv.h" + #include #include @@ -29,6 +33,154 @@ using namespace bluevk; namespace filament::backend { +namespace { + +inline VkShaderStageFlags getVkStage(backend::ShaderStage stage) { + switch(stage) { + case backend::ShaderStage::VERTEX: + return VK_SHADER_STAGE_VERTEX_BIT; + case backend::ShaderStage::FRAGMENT: + return VK_SHADER_STAGE_FRAGMENT_BIT; + case backend::ShaderStage::COMPUTE: + PANIC_POSTCONDITION("Unsupported stage"); + } +} + +} // namespace + +PushConstantDescription::PushConstantDescription(backend::Program const& program) { + mRangeCount = 0; + uint32_t offset = 0; + + // The range is laid out so that the vertex constants are defined as the first set of bytes, + // followed by fragment and compute. This means we need to keep track of the offset for each + // stage. We do the bookeeping in mDescriptions. + for (auto stage: { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) { + auto const& constants = program.getPushConstants(stage); + if (constants.empty()) { + continue; + } + + auto& description = mDescriptions[(uint8_t) stage]; + // We store the type of the constant for type-checking when writing. + description.types.reserve(constants.size()); + std::for_each(constants.cbegin(), constants.cend(), + [&description](Program::PushConstant t) { description.types.push_back(t.type); }); + + uint32_t const constantsSize = (uint32_t) constants.size() * ENTRY_SIZE; + mRanges[mRangeCount++] = { + .stageFlags = getVkStage(stage), + .offset = offset, + .size = constantsSize, + }; + description.offset = offset; + offset += constantsSize; + } +} + +void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, + backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { + + uint32_t binaryValue = 0; + auto const& description = mDescriptions[(uint8_t) stage]; + UTILS_UNUSED_IN_RELEASE auto const& types = description.types; + uint32_t const offset = description.offset; + + if (std::holds_alternative(value)) { + assert_invariant(types[index] == ConstantType::BOOL); + bool const bval = std::get(value); + binaryValue = static_cast(bval ? VK_TRUE : VK_FALSE); + } else if (std::holds_alternative(value)) { + assert_invariant(types[index] == ConstantType::FLOAT); + float const fval = std::get(value); + binaryValue = *reinterpret_cast(&fval); + } else { + assert_invariant(types[index] == ConstantType::INT); + int const ival = std::get(value); + binaryValue = *reinterpret_cast(&ival); + } + + vkCmdPushConstants(cmdbuf, layout, getVkStage(stage), offset + index * ENTRY_SIZE, ENTRY_SIZE, + &binaryValue); +} + +VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept + : HwProgram(builder.getName()), + mInfo(new(std::nothrow) PipelineInfo(builder)), + mDevice(device) { + + Program::ShaderSource const& blobs = builder.getShadersSource(); + auto& modules = mInfo->shaders; + auto const& specializationConstants = builder.getSpecializationConstants(); + std::vector shader; + + static_assert(static_cast(0) == ShaderStage::VERTEX && + static_cast(1) == ShaderStage::FRAGMENT && + MAX_SHADER_MODULES == 2); + + for (size_t i = 0; i < MAX_SHADER_MODULES; i++) { + Program::ShaderBlob const& blob = blobs[i]; + + uint32_t* data = (uint32_t*) blob.data(); + size_t dataSize = blob.size(); + + if (!specializationConstants.empty()) { + fvkutils::workaroundSpecConstant(blob, specializationConstants, shader); + data = (uint32_t*) shader.data(); + dataSize = shader.size() * 4; + } + + VkShaderModule& module = modules[i]; + VkShaderModuleCreateInfo moduleInfo = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = dataSize, + .pCode = data, + }; + VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module); + FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) + << "Unable to create shader module." + << " error=" << static_cast(result); + +#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) + utils::CString name{ builder.getName().c_str(), builder.getName().size() }; + switch (static_cast(i)) { + case ShaderStage::VERTEX: + name += "_vs"; + break; + case ShaderStage::FRAGMENT: + name += "_fs"; + break; + default: + PANIC_POSTCONDITION("Unexpected stage"); + break; + } + VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SHADER_MODULE, + reinterpret_cast(module), name.c_str()); +#endif + } + +#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) + FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0] + << ", " << modules[1] << ")"; +#endif +} + +VulkanProgram::~VulkanProgram() { + for (auto shader: mInfo->shaders) { + vkDestroyShaderModule(mDevice, shader, VKALLOC); + } + delete mInfo; +} + +void VulkanProgram::flushPushConstants(VkPipelineLayout layout) { + // At this point, we really ought to have a VkPipelineLayout. + assert_invariant(layout != VK_NULL_HANDLE); + for (const auto& c : mQueuedPushConstants) { + mInfo->pushConstantDescription.write(c.cmdbuf, layout, c.stage, c.index, c.value); + } + mQueuedPushConstants.clear(); +} + std::shared_ptr VulkanCmdFence::completed() noexcept { auto cmdFence = std::make_shared(VK_NULL_HANDLE); cmdFence->mStatus = VK_SUCCESS; diff --git a/filament/backend/src/vulkan/VulkanAsyncHandles.h b/filament/backend/src/vulkan/VulkanAsyncHandles.h index 3f31708e4ebe..7968391b5574 100644 --- a/filament/backend/src/vulkan/VulkanAsyncHandles.h +++ b/filament/backend/src/vulkan/VulkanAsyncHandles.h @@ -24,6 +24,9 @@ #include "backend/Platform.h" #include "vulkan/memory/Resource.h" +#include "vulkan/utils/StaticVector.h" + +#include #include #include @@ -35,6 +38,123 @@ namespace filament::backend { +using PushConstantNameArray = utils::FixedCapacityVector; +using PushConstantNameByStage = std::array; + +struct PushConstantDescription { + explicit PushConstantDescription(backend::Program const& program); + + VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; } + uint32_t getVkRangeCount() const noexcept { return mRangeCount; } + void write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, + uint8_t index, backend::PushConstantVariant const& value); + +private: + static constexpr uint32_t ENTRY_SIZE = sizeof(uint32_t); + + struct ConstantDescription { + utils::FixedCapacityVector types; + uint32_t offset = 0; + }; + + // Describes the constants in each shader stage. + ConstantDescription mDescriptions[Program::SHADER_TYPE_COUNT]; + VkPushConstantRange mRanges[Program::SHADER_TYPE_COUNT]; + uint32_t mRangeCount; +}; + +// Encapsulates a VkShaderModule. Note: this is a ThreadSafeResource +// because we may use parallel compilation, in which case ref counts may +// be modified on multiple backend threads. This is simply to avoid +// destroying a VkShaderModule mid-compilation of a pipeline. It is +// still assumed that all calls will originate from the backend. +struct VulkanProgram : public HwProgram, fvkmemory::ThreadSafeResource { + using BindingList = fvkutils::StaticVector; + + VulkanProgram(VkDevice device, Program const& builder) noexcept; + ~VulkanProgram(); + + /** + * Cancels any parallel compilation jobs that have not yet run for this + * program. + */ + inline void cancelParallelCompilation() { + mParallelCompilationCanceled.store(true, std::memory_order_release); + } + + /** + * Writes out any queued push constants using the provided VkPipelineLayout. + * + * @param layout The layout that is to be used along with these push constants, + * in the next draw call. + */ + void flushPushConstants(VkPipelineLayout layout); + + inline VkShaderModule getVertexShader() const { + return mInfo->shaders[0]; + } + + inline VkShaderModule getFragmentShader() const { return mInfo->shaders[1]; } + + inline uint32_t getPushConstantRangeCount() const { + return mInfo->pushConstantDescription.getVkRangeCount(); + } + + inline VkPushConstantRange const* getPushConstantRanges() const { + return mInfo->pushConstantDescription.getVkRanges(); + } + + /** + * Returns true if parallel compilation is canceled, false if not. Parallel + * compilation will be canceled if this program is destroyed before relevant + * pipelines are created. + * + * @return true if parallel compilation should run for this program, false if not + */ + inline bool isParallelCompilationCanceled() const { + return mParallelCompilationCanceled.load(std::memory_order_acquire); + } + + inline void writePushConstant(VkCommandBuffer cmdbuf, VkPipelineLayout layout, + backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { + // It's possible that we don't have the layout yet. When external samplers are used, bindPipeline() + // in VulkanDriver returns early, without binding a layout. If that happens, the layout is not + // set until draw time. Any push constants that are written during that time should be saved for + // later, and flushed when the layout is set. + if (layout != VK_NULL_HANDLE) { + mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value); + } else { + mQueuedPushConstants.push_back({cmdbuf, stage, index, value}); + } + } + + // TODO: handle compute shaders. + // The expected order of shaders - from frontend to backend - is vertex, fragment, compute. + static constexpr uint8_t const MAX_SHADER_MODULES = 2; + +private: + struct PipelineInfo { + explicit PipelineInfo(backend::Program const& program) noexcept + : pushConstantDescription(program) + {} + + VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE }; + PushConstantDescription pushConstantDescription; + }; + + struct PushConstantInfo { + VkCommandBuffer cmdbuf; + backend::ShaderStage stage; + uint8_t index; + backend::PushConstantVariant value; + }; + + PipelineInfo* mInfo; + VkDevice mDevice = VK_NULL_HANDLE; + std::atomic mParallelCompilationCanceled { false }; + std::vector mQueuedPushConstants; +}; + // Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences. struct VulkanCmdFence { explicit VulkanCmdFence(VkFence fence) : mFence(fence) { } diff --git a/filament/backend/src/vulkan/VulkanCommands.h b/filament/backend/src/vulkan/VulkanCommands.h index be68fa2ad02c..2e74dff279c6 100644 --- a/filament/backend/src/vulkan/VulkanCommands.h +++ b/filament/backend/src/vulkan/VulkanCommands.h @@ -34,10 +34,11 @@ #include #include - #include #include +#include #include +#include namespace filament::backend { @@ -72,7 +73,11 @@ struct VulkanCommandBuffer { ~VulkanCommandBuffer(); - inline void acquire(fvkmemory::resource_ptr resource) { + template || + std::is_same_v>> + inline void acquire(fvkmemory::resource_ptr resource) { mResources.push_back(resource); } @@ -115,6 +120,9 @@ struct VulkanCommandBuffer { } private: + using HeldResource = std::variant, + fvkmemory::resource_ptr>; + static uint32_t sAgeCounter; VulkanContext const& mContext; @@ -129,7 +137,7 @@ struct VulkanCommandBuffer { fvkmemory::resource_ptr mSubmission; VkFence mFence; std::shared_ptr mFenceStatus; - std::vector> mResources; + std::vector mResources; uint32_t mAge; }; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 92365b6725bc..6325d810743a 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -16,8 +16,6 @@ #include "VulkanHandles.h" -#include "VulkanConstants.h" - // TODO: remove this by moving DebugUtils out of VulkanDriver #include "VulkanDriver.h" @@ -26,7 +24,6 @@ #include "vulkan/utils/Conversion.h" #include "vulkan/utils/Definitions.h" #include "vulkan/utils/Image.h" -#include "vulkan/utils/Spirv.h" #include @@ -86,17 +83,6 @@ inline void fromStageFlags(backend::ShaderStageFlags stage, descriptor_binding_t } } -inline VkShaderStageFlags getVkStage(backend::ShaderStage stage) { - switch(stage) { - case backend::ShaderStage::VERTEX: - return VK_SHADER_STAGE_VERTEX_BIT; - case backend::ShaderStage::FRAGMENT: - return VK_SHADER_STAGE_FRAGMENT_BIT; - case backend::ShaderStage::COMPUTE: - PANIC_POSTCONDITION("Unsupported stage"); - } -} - using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask; BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) { BitmaskGroup mask; @@ -264,139 +250,6 @@ void VulkanDescriptorSet::addNewSet(VkDescriptorSet vkSet, OnRecycle&& onRecycle mSets.push_back({ vkSet, std::move(onRecycleFn) }); } -PushConstantDescription::PushConstantDescription(backend::Program const& program) { - mRangeCount = 0; - uint32_t offset = 0; - - // The range is laid out so that the vertex constants are defined as the first set of bytes, - // followed by fragment and compute. This means we need to keep track of the offset for each - // stage. We do the bookeeping in mDescriptions. - for (auto stage: { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) { - auto const& constants = program.getPushConstants(stage); - if (constants.empty()) { - continue; - } - - auto& description = mDescriptions[(uint8_t) stage]; - // We store the type of the constant for type-checking when writing. - description.types.reserve(constants.size()); - std::for_each(constants.cbegin(), constants.cend(), - [&description](Program::PushConstant t) { description.types.push_back(t.type); }); - - uint32_t const constantsSize = (uint32_t) constants.size() * ENTRY_SIZE; - mRanges[mRangeCount++] = { - .stageFlags = getVkStage(stage), - .offset = offset, - .size = constantsSize, - }; - description.offset = offset; - offset += constantsSize; - } -} - -void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, - backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { - - uint32_t binaryValue = 0; - auto const& description = mDescriptions[(uint8_t) stage]; - UTILS_UNUSED_IN_RELEASE auto const& types = description.types; - uint32_t const offset = description.offset; - - if (std::holds_alternative(value)) { - assert_invariant(types[index] == ConstantType::BOOL); - bool const bval = std::get(value); - binaryValue = static_cast(bval ? VK_TRUE : VK_FALSE); - } else if (std::holds_alternative(value)) { - assert_invariant(types[index] == ConstantType::FLOAT); - float const fval = std::get(value); - binaryValue = *reinterpret_cast(&fval); - } else { - assert_invariant(types[index] == ConstantType::INT); - int const ival = std::get(value); - binaryValue = *reinterpret_cast(&ival); - } - - vkCmdPushConstants(cmdbuf, layout, getVkStage(stage), offset + index * ENTRY_SIZE, ENTRY_SIZE, - &binaryValue); -} - -VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept - : HwProgram(builder.getName()), - mInfo(new(std::nothrow) PipelineInfo(builder)), - mDevice(device) { - - Program::ShaderSource const& blobs = builder.getShadersSource(); - auto& modules = mInfo->shaders; - auto const& specializationConstants = builder.getSpecializationConstants(); - std::vector shader; - - static_assert(static_cast(0) == ShaderStage::VERTEX && - static_cast(1) == ShaderStage::FRAGMENT && - MAX_SHADER_MODULES == 2); - - for (size_t i = 0; i < MAX_SHADER_MODULES; i++) { - Program::ShaderBlob const& blob = blobs[i]; - - uint32_t* data = (uint32_t*) blob.data(); - size_t dataSize = blob.size(); - - if (!specializationConstants.empty()) { - fvkutils::workaroundSpecConstant(blob, specializationConstants, shader); - data = (uint32_t*) shader.data(); - dataSize = shader.size() * 4; - } - - VkShaderModule& module = modules[i]; - VkShaderModuleCreateInfo moduleInfo = { - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .codeSize = dataSize, - .pCode = data, - }; - VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module); - FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) - << "Unable to create shader module." - << " error=" << static_cast(result); - -#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) - utils::CString name{ builder.getName().c_str(), builder.getName().size() }; - switch (static_cast(i)) { - case ShaderStage::VERTEX: - name += "_vs"; - break; - case ShaderStage::FRAGMENT: - name += "_fs"; - break; - default: - PANIC_POSTCONDITION("Unexpected stage"); - break; - } - VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SHADER_MODULE, - reinterpret_cast(module), name.c_str()); -#endif - } - -#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) - FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0] - << ", " << modules[1] << ")"; -#endif -} - -VulkanProgram::~VulkanProgram() { - for (auto shader: mInfo->shaders) { - vkDestroyShaderModule(mDevice, shader, VKALLOC); - } - delete mInfo; -} - -void VulkanProgram::flushPushConstants(VkPipelineLayout layout) { - // At this point, we really ought to have a VkPipelineLayout. - assert_invariant(layout != VK_NULL_HANDLE); - for (const auto& c : mQueuedPushConstants) { - mInfo->pushConstantDescription.write(c.cmdbuf, layout, c.stage, c.index, c.value); - } - mQueuedPushConstants.clear(); -} - // Creates a special "default" render target (i.e. associated with the swap chain) VulkanRenderTarget::VulkanRenderTarget() : HwRenderTarget(0, 0), diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 9a4f1de92fb3..76b04dbf1374 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -30,9 +30,6 @@ #include "vulkan/memory/Resource.h" #include "vulkan/memory/ResourcePointer.h" #include "vulkan/utils/Definitions.h" -#include "vulkan/utils/StaticVector.h" - -#include #include #include @@ -241,118 +238,6 @@ struct VulkanMemoryMappedBuffer : public HwMemoryMappedBuffer, Resource { uint32_t offset = 0; }; -using PushConstantNameArray = utils::FixedCapacityVector; -using PushConstantNameByStage = std::array; - -struct PushConstantDescription { - explicit PushConstantDescription(backend::Program const& program); - - VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; } - uint32_t getVkRangeCount() const noexcept { return mRangeCount; } - void write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage, - uint8_t index, backend::PushConstantVariant const& value); - -private: - static constexpr uint32_t ENTRY_SIZE = sizeof(uint32_t); - - struct ConstantDescription { - utils::FixedCapacityVector types; - uint32_t offset = 0; - }; - - // Describes the constants in each shader stage. - ConstantDescription mDescriptions[Program::SHADER_TYPE_COUNT]; - VkPushConstantRange mRanges[Program::SHADER_TYPE_COUNT]; - uint32_t mRangeCount; -}; - -struct VulkanProgram : public HwProgram, fvkmemory::Resource { - using BindingList = fvkutils::StaticVector; - - VulkanProgram(VkDevice device, Program const& builder) noexcept; - ~VulkanProgram(); - - /** - * Cancels any parallel compilation jobs that have not yet run for this - * program. - */ - inline void cancelParallelCompilation() { - mParallelCompilationCanceled.store(true, std::memory_order_release); - } - - /** - * Writes out any queued push constants using the provided VkPipelineLayout. - * - * @param layout The layout that is to be used along with these push constants, - * in the next draw call. - */ - void flushPushConstants(VkPipelineLayout layout); - - inline VkShaderModule getVertexShader() const { - return mInfo->shaders[0]; - } - - inline VkShaderModule getFragmentShader() const { return mInfo->shaders[1]; } - - inline uint32_t getPushConstantRangeCount() const { - return mInfo->pushConstantDescription.getVkRangeCount(); - } - - inline VkPushConstantRange const* getPushConstantRanges() const { - return mInfo->pushConstantDescription.getVkRanges(); - } - - /** - * Returns true if parallel compilation is canceled, false if not. Parallel - * compilation will be canceled if this program is destroyed before relevant - * pipelines are created. - * - * @return true if parallel compilation should run for this program, false if not - */ - inline bool isParallelCompilationCanceled() const { - return mParallelCompilationCanceled.load(std::memory_order_acquire); - } - - inline void writePushConstant(VkCommandBuffer cmdbuf, VkPipelineLayout layout, - backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) { - // It's possible that we don't have the layout yet. When external samplers are used, bindPipeline() - // in VulkanDriver returns early, without binding a layout. If that happens, the layout is not - // set until draw time. Any push constants that are written during that time should be saved for - // later, and flushed when the layout is set. - if (layout != VK_NULL_HANDLE) { - mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value); - } else { - mQueuedPushConstants.push_back({cmdbuf, stage, index, value}); - } - } - - // TODO: handle compute shaders. - // The expected order of shaders - from frontend to backend - is vertex, fragment, compute. - static constexpr uint8_t const MAX_SHADER_MODULES = 2; - -private: - struct PipelineInfo { - explicit PipelineInfo(backend::Program const& program) noexcept - : pushConstantDescription(program) - {} - - VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE }; - PushConstantDescription pushConstantDescription; - }; - - struct PushConstantInfo { - VkCommandBuffer cmdbuf; - backend::ShaderStage stage; - uint8_t index; - backend::PushConstantVariant value; - }; - - PipelineInfo* mInfo; - VkDevice mDevice = VK_NULL_HANDLE; - std::atomic mParallelCompilationCanceled { false }; - std::vector mQueuedPushConstants; -}; - // The render target bundles together a set of attachments, each of which can have one of the // following ownership semantics: // diff --git a/filament/backend/src/vulkan/memory/Resource.h b/filament/backend/src/vulkan/memory/Resource.h index 96eaac8aab78..6e924976847f 100644 --- a/filament/backend/src/vulkan/memory/Resource.h +++ b/filament/backend/src/vulkan/memory/Resource.h @@ -67,7 +67,10 @@ ResourceType getTypeEnum() noexcept; std::string_view getTypeStr(ResourceType type); inline bool isThreadSafeType(ResourceType type) { - return type == ResourceType::FENCE || type == ResourceType::TIMER_QUERY; + return type == ResourceType::PROGRAM || + type == ResourceType::FENCE || + type == ResourceType::TIMER_QUERY || + type == ResourceType::SYNC; } struct Resource { diff --git a/filament/backend/src/vulkan/memory/ResourceManager.h b/filament/backend/src/vulkan/memory/ResourceManager.h index 12a6ade19d58..5e2a32bf9d08 100644 --- a/filament/backend/src/vulkan/memory/ResourceManager.h +++ b/filament/backend/src/vulkan/memory/ResourceManager.h @@ -49,8 +49,11 @@ class ResourceManager { using AllocatorImpl = HandleAllocatorVK; template - using requires_thread_safety = typename std::disjunction, - std::is_same>; + using requires_thread_safety = typename std::disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same>; template inline D* construct(Handle const& handle, ARGS&&... args) noexcept {