diff --git a/filament/backend/include/backend/platforms/VulkanPlatform.h b/filament/backend/include/backend/platforms/VulkanPlatform.h index 2bbe1983f2..91189310d5 100644 --- a/filament/backend/include/backend/platforms/VulkanPlatform.h +++ b/filament/backend/include/backend/platforms/VulkanPlatform.h @@ -88,6 +88,7 @@ public: VkFormat colorFormat = VK_FORMAT_UNDEFINED; VkFormat depthFormat = VK_FORMAT_UNDEFINED; VkExtent2D extent = {0, 0}; + bool isProtected = false; }; struct ImageSyncData { @@ -199,6 +200,13 @@ public: */ virtual bool hasResized(SwapChainPtr handle); + /** + * Check if the surface is protected. + * @param handle The handle returned by createSwapChain() + * @return Whether the swapchain is protected + */ + virtual bool isProtected(SwapChainPtr handle); + /** * Carry out a recreation of the swapchain. * @param handle The handle returned by createSwapChain() @@ -267,6 +275,23 @@ public: */ VkQueue getGraphicsQueue() const noexcept; + /** + * @return The family index of the protected graphics queue selected for the + * Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept; + + /** + * @return The index of the protected graphics queue (if there are multiple + * graphics queues) selected for the Vulkan backend. + */ + uint32_t getProtectedGraphicsQueueIndex() const noexcept; + + /** + * @return The protected queue that was selected for the Vulkan backend. + */ + VkQueue getProtectedGraphicsQueue() const noexcept; + private: static ExtensionSet getSwapchainInstanceExtensions(); diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 8528bb77d0..f196f58a9f 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1794,6 +1794,16 @@ void MetalDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets) { + + if (UTILS_UNLIKELY(!dsh)) { + DEBUG_LOG("bindDescriptorSet(dsh = null, set = %d, offsets = [])\n", set); + mContext->currentDescriptorSets[set] = nullptr; + mContext->vertexDescriptorBindings.setBuffer(nil, 0, set); + mContext->fragmentDescriptorBindings.setBuffer(nil, 0, set); + mContext->dynamicOffsets.setOffsets(set, nullptr, 0); + return; + } + auto descriptorSet = handle_cast(dsh); const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount(); utils::FixedCapacityVector offsetsVector(dynamicBindings, 0); diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index a606d09087..2154a26964 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -3875,6 +3875,14 @@ void OpenGLDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets) { + + if (UTILS_UNLIKELY(!dsh)) { + mBoundDescriptorSets[set].dsh = dsh; + mInvalidDescriptorSetBindings.set(set, true); + mInvalidDescriptorSetBindingOffsets.set(set, true); + return; + } + // handle_cast<> here also serves to validate the handle (it actually cannot return nullptr) GLDescriptorSet const* const ds = handle_cast(dsh); if (ds) { diff --git a/filament/backend/src/vulkan/VulkanContext.h b/filament/backend/src/vulkan/VulkanContext.h index e7a1814196..68c44dfdce 100644 --- a/filament/backend/src/vulkan/VulkanContext.h +++ b/filament/backend/src/vulkan/VulkanContext.h @@ -93,6 +93,9 @@ struct VulkanRenderPass { struct VulkanContext { public: inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const { + if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) { + assert_invariant(isProtectedMemorySupported() == true); + } for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) { if (flags & 1) { if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) { @@ -113,19 +116,19 @@ public: } inline VkPhysicalDeviceLimits const& getPhysicalDeviceLimits() const noexcept { - return mPhysicalDeviceProperties.limits; + return mPhysicalDeviceProperties.properties.limits; } inline uint32_t getPhysicalDeviceVendorId() const noexcept { - return mPhysicalDeviceProperties.vendorID; + return mPhysicalDeviceProperties.properties.vendorID; } inline bool isImageCubeArraySupported() const noexcept { - return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE; + return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE; } inline bool isDepthClampSupported() const noexcept { - return mPhysicalDeviceFeatures.depthClamp == VK_TRUE; + return mPhysicalDeviceFeatures.features.depthClamp == VK_TRUE; } inline bool isDebugMarkersSupported() const noexcept { @@ -141,21 +144,30 @@ public: } inline bool isClipDistanceSupported() const noexcept { - return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE; + return mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE; } inline bool isLazilyAllocatedMemorySupported() const noexcept { return mLazilyAllocatedMemorySupported; } + inline bool isProtectedMemorySupported() const noexcept { + return mProtectedMemorySupported; + } + private: VkPhysicalDeviceMemoryProperties mMemoryProperties = {}; - VkPhysicalDeviceProperties mPhysicalDeviceProperties = {}; - VkPhysicalDeviceFeatures mPhysicalDeviceFeatures = {}; + VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 + }; + VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 + }; bool mDebugMarkersSupported = false; bool mDebugUtilsSupported = false; bool mMultiviewEnabled = false; bool mLazilyAllocatedMemorySupported = false; + bool mProtectedMemorySupported = false; VkFormatList mDepthStencilFormats; VkFormatList mBlittableDepthStencilFormats; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 492f0c24b9..567a34bc53 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -977,8 +977,7 @@ bool VulkanDriver::isSRGBSwapChainSupported() { } bool VulkanDriver::isProtectedContentSupported() { - // the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported - return false; + return mContext.isProtectedMemorySupported(); } bool VulkanDriver::isStereoSupported() { @@ -1767,8 +1766,12 @@ void VulkanDriver::bindDescriptorSet( backend::DescriptorSetHandle dsh, backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) { - VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); - mDescriptorSetManager.bind(setIndex, set, std::move(offsets)); + if (dsh) { + VulkanDescriptorSet* set = mResourceAllocator.handle_cast(dsh); + mDescriptorSetManager.bind(setIndex, set, std::move(offsets)); + } else { + mDescriptorSetManager.unbind(setIndex); + } } void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) { diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 954dd77f5e..43c05c6904 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -256,6 +256,7 @@ VulkanRenderTarget::VulkanRenderTarget() : HwRenderTarget(0, 0), VulkanResource(VulkanResourceType::RENDER_TARGET), mOffscreen(false), + mProtected(false), mResources(nullptr), mInfo(std::make_unique()) { mInfo->rpkey.samples = mInfo->fbkey.samples = 1; @@ -267,6 +268,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) { VkExtent2D const extent = swapChain.getExtent(); width = extent.width; height = extent.height; + mProtected = swapChain.isProtected(); VulkanAttachment color = {.texture = swapChain.getCurrentColor()}; mInfo->attachments = {color}; @@ -303,6 +305,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica : HwRenderTarget(width, height), VulkanResource(VulkanResourceType::RENDER_TARGET), mOffscreen(true), + mProtected(false), mResources(handleAllocator), mInfo(std::make_unique()) { diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index b74b90b89c..60d3702a72 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -306,6 +306,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource { inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; } inline bool isSwapChain() const { return !mOffscreen; } + inline bool isProtected() const { return mProtected; } void bindToSwapChain(VulkanSwapChain& surf); @@ -328,6 +329,7 @@ private: int8_t msaaIndex = UNDEFINED_INDEX; }; bool const mOffscreen; + bool mProtected; VulkanAcquireOnlyResourceManager mResources; std::unique_ptr mInfo; diff --git a/filament/backend/src/vulkan/VulkanSwapChain.cpp b/filament/backend/src/vulkan/VulkanSwapChain.cpp index 4989b925a3..bf9fd353cd 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.cpp +++ b/filament/backend/src/vulkan/VulkanSwapChain.cpp @@ -63,14 +63,20 @@ void VulkanSwapChain::update() { mColors.reserve(bundle.colors.size()); VkDevice const device = mPlatform->getDevice(); + TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT; + TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT; + if (bundle.isProtected) { + depthUsage |= TextureUsage::PROTECTED; + colorUsage |= TextureUsage::PROTECTED; + } for (auto const color: bundle.colors) { mColors.push_back(std::make_unique(device, mAllocator, mCommands, mHandleAllocator, color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, - TextureUsage::COLOR_ATTACHMENT, mStagePool, true /* heap allocated */)); + colorUsage, mStagePool, true /* heap allocated */)); } mDepth = std::make_unique(device, mAllocator, mCommands, mHandleAllocator, bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, - TextureUsage::DEPTH_ATTACHMENT, mStagePool, true /* heap allocated */); + depthUsage, mStagePool, true /* heap allocated */); mExtent = bundle.extent; } diff --git a/filament/backend/src/vulkan/VulkanSwapChain.h b/filament/backend/src/vulkan/VulkanSwapChain.h index b3e49c044e..acb88c56b9 100644 --- a/filament/backend/src/vulkan/VulkanSwapChain.h +++ b/filament/backend/src/vulkan/VulkanSwapChain.h @@ -74,6 +74,9 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource { return mExtent; } + inline bool isProtected() noexcept { + return mPlatform->isProtected(swapChain); + } private: static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS; diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp index 2358d6ea0d..a526310118 100644 --- a/filament/backend/src/vulkan/VulkanTexture.cpp +++ b/filament/backend/src/vulkan/VulkanTexture.cpp @@ -147,12 +147,14 @@ inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) { VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format, - VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout) + VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout, + bool isProtected) : VulkanResource(VulkanResourceType::HEAP_ALLOCATED), mVkFormat(format), mViewType(viewType), mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount}, mDefaultLayout(defaultLayout), + mIsProtected(isProtected), mStagePool(stagePool), mDevice(device), mAllocator(allocator), @@ -183,7 +185,8 @@ VulkanTexture::VulkanTexture( mState(handleAllocator->initHandle( device, allocator, commands, stagePool, format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1, - getDefaultLayoutImpl(tusage))) { + getDefaultLayoutImpl(tusage), + any(usage& TextureUsage::PROTECTED))) { auto* const state = getSharedState(); state->mTextureImage = image; mPrimaryViewRange = state->mFullViewRange; @@ -201,7 +204,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, mAllocator(handleAllocator), mState(handleAllocator->initHandle(device, allocator, commands, stagePool, backend::getVkFormat(tformat), imgutil::getViewType(target), levels, - getLayerCount(target, depth), VulkanLayout::UNDEFINED)) { + getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) { auto* const state = getSharedState(); // Create an appropriately-sized device-only VkImage, but do not fill it yet. @@ -232,6 +235,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, imageInfo.arrayLayers = depth * 6; imageInfo.extent.depth = 1; } + if (any(usage & TextureUsage::PROTECTED)) { + imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT; + } if (any(usage & TextureUsage::BLIT_SRC)) { imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; @@ -331,7 +337,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, const VkFlags requiredMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | - (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U); + (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) | + (state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U); uint32_t memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags); @@ -416,6 +423,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP || target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1)); auto* const state = getSharedState(); + assert_invariant(state->mIsProtected == false); const PixelBufferDescriptor* hostData = &data; PixelBufferDescriptor reshapedData; diff --git a/filament/backend/src/vulkan/VulkanTexture.h b/filament/backend/src/vulkan/VulkanTexture.h index acc5884670..5165b1cec7 100644 --- a/filament/backend/src/vulkan/VulkanTexture.h +++ b/filament/backend/src/vulkan/VulkanTexture.h @@ -35,7 +35,7 @@ class VulkanResourceAllocator; struct VulkanTextureState : public VulkanResource { VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels, - uint8_t layerCount, VulkanLayout defaultLayout); + uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected); struct ImageViewKey { VkImageSubresourceRange range; // 4 * 5 bytes @@ -70,6 +70,8 @@ struct VulkanTextureState : public VulkanResource { VkImage mTextureImage = VK_NULL_HANDLE; VulkanLayout mDefaultLayout; + bool mIsProtected = false; + // Track the image layout of each subresource using a sparse range map. utils::RangeMap mSubresourceLayouts; @@ -179,6 +181,11 @@ struct VulkanTexture : public HwTexture, VulkanResource { return state->mIsTransientAttachment; } + bool getIsProtected() const { + VulkanTextureState const* state = getSharedState(); + return state->mIsProtected; + } + bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range, VulkanLayout newLayout); diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp index 28bf06dc2d..0533b58e0f 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.cpp @@ -369,6 +369,10 @@ void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set mStashedSets[setIndex] = set; } +void VulkanDescriptorSetManager::unbind(uint8_t setIndex) { + mStashedSets[setIndex] = nullptr; +} + void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) { // setMask indicates the set of descriptor sets the driver wants to bind, curMask is the diff --git a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h index 75e8afda61..2735e5062b 100644 --- a/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h +++ b/filament/backend/src/vulkan/caching/VulkanDescriptorSetManager.h @@ -61,6 +61,8 @@ public: void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets); + void unbind(uint8_t setIndex); + void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask); diff --git a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp index 6ddfb94bd3..c894e18bff 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatform.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatform.cpp @@ -300,10 +300,10 @@ VkInstance createInstance(ExtensionSet const& requiredExts) { } VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, - VkPhysicalDeviceFeatures const& features, uint32_t graphicsQueueFamilyIndex, - ExtensionSet const& deviceExtensions) { + VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex, + uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions) { VkDevice device; - VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {}; + VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {}; const float queuePriority[] = {1.0f}; VkDeviceCreateInfo deviceCreateInfo = {}; FixedCapacityVector requestExtensions; @@ -314,22 +314,30 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, for (auto const& ext: deviceExtensions) { requestExtensions.push_back(ext.data()); } - deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfo->queueFamilyIndex = graphicsQueueFamilyIndex; - deviceQueueCreateInfo->queueCount = 1; - deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0]; + deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex; + deviceQueueCreateInfo[0].queueCount = 1; + deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority[0]; + // Protected queue + deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT; + deviceQueueCreateInfo[1].queueFamilyIndex = protectedGraphicsQueueFamilyIndex; + deviceQueueCreateInfo[1].queueCount = 1; + deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority[0]; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = 1; + bool const hasProtectedQueue = protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX; + deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1; deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; // We could simply enable all supported features, but since that may have performance // consequences let's just enable the features we need. VkPhysicalDeviceFeatures enabledFeatures{ - .depthClamp = features.depthClamp, - .samplerAnisotropy = features.samplerAnisotropy, - .textureCompressionETC2 = features.textureCompressionETC2, - .textureCompressionBC = features.textureCompressionBC, - .shaderClipDistance = features.shaderClipDistance, + .depthClamp = features.features.depthClamp, + .samplerAnisotropy = features.features.samplerAnisotropy, + .textureCompressionETC2 = features.features.textureCompressionETC2, + .textureCompressionBC = features.features.textureCompressionBC, + .shaderClipDistance = features.features.shaderClipDistance, }; deviceCreateInfo.pEnabledFeatures = &enabledFeatures; @@ -360,6 +368,16 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, pNext = &multiview; } + VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, + }; + if (hasProtectedQueue) { + // Enable protected memory, if requested. + protectedMemory.protectedMemory = VK_TRUE; + protectedMemory.pNext = pNext; + pNext = &protectedMemory; + } + deviceCreateInfo.pNext = pNext; VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device); @@ -406,13 +424,13 @@ FixedCapacityVector getPhysicalDeviceQueueFamilyPropert return queueFamiliesProperties; } -uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice) { +uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags) { const FixedCapacityVector queueFamiliesProperties = getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice); uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX; for (uint32_t j = 0; j < queueFamiliesProperties.size(); ++j) { VkQueueFamilyProperties props = queueFamiliesProperties[j]; - if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + if (props.queueCount != 0 && props.queueFlags & flags) { graphicsQueueFamilyIndex = j; break; } @@ -473,7 +491,8 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance, // Does the device have any command queues that support graphics? // In theory, we should also ensure that the device supports presentation of our // particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement. - if (identifyGraphicsQueueFamilyIndex(candidateDevice) == INVALID_VK_INDEX) { + if (identifyGraphicsQueueFamilyIndex(candidateDevice, VK_QUEUE_GRAPHICS_BIT) + == INVALID_VK_INDEX) { continue; } @@ -584,6 +603,9 @@ struct VulkanPlatformPrivate { uint32_t mGraphicsQueueFamilyIndex = INVALID_VK_INDEX; uint32_t mGraphicsQueueIndex = INVALID_VK_INDEX; VkQueue mGraphicsQueue = VK_NULL_HANDLE; + uint32_t mProtectedGraphicsQueueFamilyIndex = INVALID_VK_INDEX; + uint32_t mProtectedGraphicsQueueIndex = INVALID_VK_INDEX; + VkQueue mProtectedGraphicsQueue = VK_NULL_HANDLE; VulkanContext mContext = {}; // We use a map to both map a handle (i.e. SwapChainPtr) to the concrete type and also to @@ -686,22 +708,45 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice); + VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, + }; + // Chain to the pNext linked list + queryProtectedMemoryFeatures.pNext = context.mPhysicalDeviceFeatures.pNext; + context.mPhysicalDeviceFeatures.pNext = &queryProtectedMemoryFeatures; + VkPhysicalDeviceProtectedMemoryProperties protectedMemoryProperties = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, + }; + protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext; + context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties; + // Initialize the following fields: physicalDeviceProperties, memoryProperties, // physicalDeviceFeatures, graphicsQueueFamilyIndex. - vkGetPhysicalDeviceProperties(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties); - vkGetPhysicalDeviceFeatures(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures); + vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties); + vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures); vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties); mImpl->mGraphicsQueueFamilyIndex = mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX - ? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice) + ? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, + VK_QUEUE_GRAPHICS_BIT) : mImpl->mGraphicsQueueFamilyIndex; assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX); + // We know we need to allocate the protected version of the VK objects + context.mProtectedMemorySupported = + static_cast(queryProtectedMemoryFeatures.protectedMemory); + if (context.mProtectedMemorySupported) { + mImpl->mProtectedGraphicsQueueFamilyIndex + = identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, + (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT)); + assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX); + } + // Only enable shaderClipDistance if we are doing instanced stereoscopic rendering. - if (context.mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE + if (context.mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE && driverConfig.stereoscopicType != StereoscopicType::INSTANCED) { - context.mPhysicalDeviceFeatures.shaderClipDistance = VK_FALSE; + context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE; } // At this point, we should have a family index that points to a family that has > 0 queues for @@ -711,6 +756,11 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, mImpl->mGraphicsQueueIndex = mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex; + // Applying the same logic to the protected queue index (Not sure about shared context and protection) + mImpl->mProtectedGraphicsQueueIndex + = mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : + mImpl->mProtectedGraphicsQueueIndex; + ExtensionSet deviceExts; // If using a shared context, we do not assume any extensions. if (!mImpl->mSharedContext) { @@ -727,7 +777,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, mImpl->mDevice = mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice, - context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex, deviceExts) + context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex, + mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts) : mImpl->mDevice; assert_invariant(mImpl->mDevice != VK_NULL_HANDLE); @@ -735,6 +786,15 @@ Driver* VulkanPlatform::createDriver(void* sharedContext, &mImpl->mGraphicsQueue); assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE); + if (context.mProtectedMemorySupported) { + VkDeviceQueueInfo2 info = {}; + info.queueFamilyIndex = mImpl->mProtectedGraphicsQueueFamilyIndex; + info.queueIndex = mImpl->mProtectedGraphicsQueueIndex; + info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2; + info.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT; + vkGetDeviceQueue2(mImpl->mDevice, &info, &mImpl->mProtectedGraphicsQueue); + } + // Store the extension support in the context context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); @@ -800,6 +860,10 @@ bool VulkanPlatform::hasResized(SwapChainPtr handle) { SWAPCHAIN_RET_FUNC(hasResized, handle, ) } +bool VulkanPlatform::isProtected(SwapChainPtr handle) { + SWAPCHAIN_RET_FUNC(isProtected, handle, ) +} + VkResult VulkanPlatform::recreate(SwapChainPtr handle) { SWAPCHAIN_RET_FUNC(recreate, handle, ) } @@ -829,6 +893,13 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags, flags |= SWAP_CHAIN_CONFIG_ENABLE_XCB; } + if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) { + if (!mImpl->mContext.mProtectedMemorySupported) { + utils::slog.w << "protected swapchain requested, but VulkanPlatform does not support it" + << utils::io::endl; + } + } + auto [surface, fallbackExtent] = createVkSurfaceKHR(nativeWindow, mImpl->mInstance, flags); // The VulkanPlatformSurfaceSwapChain now `owns` the surface. VulkanPlatformSurfaceSwapChain* swapchain = new VulkanPlatformSurfaceSwapChain(mImpl->mContext, @@ -862,6 +933,18 @@ VkQueue VulkanPlatform::getGraphicsQueue() const noexcept { return mImpl->mGraphicsQueue; } +uint32_t VulkanPlatform::getProtectedGraphicsQueueFamilyIndex() const noexcept { + return mImpl->mProtectedGraphicsQueueFamilyIndex; +} + +uint32_t VulkanPlatform::getProtectedGraphicsQueueIndex() const noexcept { + return mImpl->mProtectedGraphicsQueueIndex; +} + +VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept { + return mImpl->mProtectedGraphicsQueue; +} + #undef SWAPCHAIN_RET_FUNC }// namespace filament::backend diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp index d8cace97a6..3037f64af7 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp @@ -125,7 +125,8 @@ VulkanPlatformSurfaceSwapChain::VulkanPlatformSurfaceSwapChain(VulkanContext con mSurface(surface), mFallbackExtent(fallbackExtent), mUsesRGB((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0), - mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0) { + mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0), + mIsProtected((flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) != 0) { assert_invariant(surface); create(); } @@ -212,6 +213,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() { VkSwapchainCreateInfoKHR const createInfo{ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : + VkSwapchainCreateFlagsKHR(0), .surface = mSurface, .minImageCount = desiredImageCount, .imageFormat = surfaceFormat.format, @@ -246,13 +249,15 @@ VkResult VulkanPlatformSurfaceSwapChain::create() { mSwapChainBundle.depthFormat = selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil); mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat); + mSwapChainBundle.isProtected = mIsProtected; FVK_LOGI << "vkCreateSwapchain" << ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", " << surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", " << "swapchain-size=" << mSwapChainBundle.colors.size() << ", " << "identity-transform=" << (caps.currentTransform == 1) << ", " - << "depth=" << mSwapChainBundle.depthFormat + << "depth=" << mSwapChainBundle.depthFormat << ", " + << "protected=" << mSwapChainBundle.isProtected << io::endl; VkSemaphoreCreateInfo const semaphoreCreateInfo = { @@ -317,6 +322,10 @@ bool VulkanPlatformSurfaceSwapChain::hasResized() { return !equivalent(mSwapChainBundle.extent, perceivedExtent); } +bool VulkanPlatformSurfaceSwapChain::isProtected() { + return mIsProtected; +} + // Non-virtual override VkResult VulkanPlatformSurfaceSwapChain::recreate() { destroy(); diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h index bcad1ccb0e..2a715a210c 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h +++ b/filament/backend/src/vulkan/platform/VulkanPlatformSwapChainImpl.h @@ -64,6 +64,11 @@ struct VulkanPlatformSwapChainImpl : public Platform::SwapChain { return false; } + // Non-virtual override-able method + bool isProtected() { + return false; + } + protected: // Non-virtual override-able method void destroy(); @@ -97,6 +102,9 @@ struct VulkanPlatformSurfaceSwapChain : public VulkanPlatformSwapChainImpl { // Non-virtual override-able method bool hasResized(); + // Non-virtual override-able method + bool isProtected(); + protected: // Non-virtual override-able method void destroy(); @@ -117,6 +125,7 @@ private: bool mUsesRGB = false; bool mHasStencil = false; + bool mIsProtected = false; bool mSuboptimal; }; diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index ec10f0abda..90c743e584 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -519,16 +519,17 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph 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); - RenderPass const pass{ passBuilder.build(mEngine) }; - RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params); + RenderPass const pass{ passBuilder.build(mEngine, driver) }; + auto out = resources.getRenderPassInfo(); + driver.beginRenderPass(out.target, out.params); + pass.getExecutor().execute(mEngine, driver); + driver.endRenderPass(); }); auto depth = structurePass->depth; @@ -678,8 +679,10 @@ FrameGraphId PostProcessManager::ssr(FrameGraph& fg, // generate all our drawing commands, except blended objects. passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS); - RenderPass const pass{ passBuilder.build(mEngine) }; - RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params); + RenderPass const pass{ passBuilder.build(mEngine, driver) }; + driver.beginRenderPass(out.target, out.params); + pass.getExecutor().execute(mEngine, driver); + driver.endRenderPass(); }); return ssrPass->reflections; diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 9498184c86..251d00e89e 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -68,7 +68,6 @@ namespace filament { using namespace backend; RenderPassBuilder& RenderPassBuilder::customCommand( - FEngine& engine, uint8_t channel, RenderPass::Pass pass, RenderPass::CustomCommand custom, @@ -76,17 +75,17 @@ RenderPassBuilder& RenderPassBuilder::customCommand( RenderPass::Executor::CustomCommandFn const& command) { if (!mCustomCommands.has_value()) { // construct the vector the first time - mCustomCommands.emplace(engine.getPerRenderPassArena()); + mCustomCommands.emplace(); } mCustomCommands->emplace_back(channel, pass, custom, order, command); return *this; } -RenderPass RenderPassBuilder::build(FEngine& engine) { +RenderPass RenderPassBuilder::build(FEngine const& engine, backend::DriverApi& driver) const { assert_invariant(mRenderableSoa); assert_invariant(mScissorViewport.width <= std::numeric_limits::max()); assert_invariant(mScissorViewport.height <= std::numeric_limits::max()); - return RenderPass{ engine, *this }; + return RenderPass{ engine, driver, *this }; } // ------------------------------------------------------------------------------------------------ @@ -107,11 +106,11 @@ void RenderPass::DescriptorSetHandleDeleter::operator()( // ------------------------------------------------------------------------------------------------ -RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept +RenderPass::RenderPass(FEngine const& engine, backend::DriverApi& driver, + RenderPassBuilder const& builder) noexcept : mRenderableSoa(*builder.mRenderableSoa), mColorPassDescriptorSet(builder.mColorPassDescriptorSet), - mScissorViewport(builder.mScissorViewport), - mCustomCommands(engine.getPerRenderPassArena()) { + mScissorViewport(builder.mScissorViewport) { // compute the number of commands we need updateSummedPrimitiveCounts( @@ -127,10 +126,12 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce uint32_t const customCommandCount = builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0; + // FIXME: builder.mArena must be thread safe eventually Command* const commandBegin = builder.mArena.alloc(commandCount + customCommandCount); Command* commandEnd = commandBegin + (commandCount + customCommandCount); assert_invariant(commandBegin); + // FIXME: builder.mArena must be thread safe eventually if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) { static bool sLogOnce = true; if (UTILS_UNLIKELY(sLogOnce)) { @@ -150,6 +151,7 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce builder.mCameraForwardVector); if (builder.mCustomCommands.has_value()) { + mCustomCommands.reserve(customCommandCount); Command* p = commandBegin + commandCount; for (auto const& [channel, passId, command, order, fn]: builder.mCustomCommands.value()) { appendCustomCommand(p++, channel, passId, command, order, fn); @@ -166,7 +168,9 @@ RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexce stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount; } commandEnd = resize(builder.mArena, - instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount)); + instanceify(driver, + engine.getPerRenderableDescriptorSetLayout().getHandle(), + commandBegin, commandEnd, stereoscopicEyeCount)); } // these are `const` from this point on... @@ -182,7 +186,7 @@ RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexc return last; } -void RenderPass::appendCommands(FEngine& engine, +void RenderPass::appendCommands(FEngine const& engine, Slice commands, utils::Range const vr, CommandTypeFlags const commandTypeFlags, @@ -283,17 +287,8 @@ RenderPass::Command* RenderPass::sortCommands( return last; } -void RenderPass::execute(RenderPass const& pass, - FEngine& engine, const char* name, - backend::Handle renderTarget, - backend::RenderPassParams params) noexcept { - DriverApi& driver = engine.getDriverApi(); - driver.beginRenderPass(renderTarget, params); - pass.getExecutor().execute(engine, name); - driver.endRenderPass(); -} - -RenderPass::Command* RenderPass::instanceify(FEngine& engine, +RenderPass::Command* RenderPass::instanceify(backend::DriverApi& driver, + DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle, Command* curr, Command* const last, int32_t eyeCount) const noexcept { SYSTRACE_NAME("instanceify"); @@ -352,8 +347,6 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine, 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, @@ -382,8 +375,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine, // 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.createDescriptorSet(perRenderableDescriptorSetLayoutHandle), driver }; driver.updateDescriptorSetBuffer(mInstancedDescriptorSetHandle, @@ -418,10 +410,7 @@ RenderPass::Command* RenderPass::instanceify(FEngine& engine, if (UTILS_UNLIKELY(firstSentinel)) { //slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of " // << count << io::endl; - // we have instanced primitives - DriverApi& driver = engine.getDriverApi(); - // copy our instanced ubo data driver.updateBufferObjectUnsynchronized(mInstancedUboHandle, { stagingBuffer, sizeof(PerRenderableData) * instancedPrimitiveOffset, @@ -866,8 +855,8 @@ void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noe mScissor = scissor; } -void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept { - execute(engine, mCommands.begin(), mCommands.end()); +void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& driver) const noexcept { + execute(engine, driver, mCommands.begin(), mCommands.end()); } UTILS_NOINLINE // no need to be inlined @@ -893,14 +882,12 @@ backend::Viewport RenderPass::Executor::applyScissorViewport( } UTILS_NOINLINE // no need to be inlined -void RenderPass::Executor::execute(FEngine& engine, - const Command* first, const Command* last) const noexcept { +void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& driver, + Command const* first, Command const* last) const noexcept { SYSTRACE_CALL(); SYSTRACE_CONTEXT(); - DriverApi& driver = engine.getDriverApi(); - size_t const capacity = engine.getMinCommandBufferSize(); CircularBuffer const& circularBuffer = driver.getCircularBuffer(); @@ -965,7 +952,9 @@ void RenderPass::Executor::execute(FEngine& engine, // check we have enough capacity to write these commandCount commands, if not, // request a new CircularBuffer allocation of `capacity` bytes. if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity - commandSizeInBytes)) { - engine.flush(); // TODO: we should use a "fast" flush if possible + // FIXME: eventually we can't flush here because this will be a secondary + // command buffer. We will need another solution for overflows. + const_cast(engine).flush(); } first--; @@ -1093,7 +1082,9 @@ void RenderPass::Executor::execute(FEngine& engine, // If the remaining space is less than half the capacity, we flush right away to // allow some headroom for commands that might come later. if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity / 2)) { - engine.flush(); + // FIXME: eventually we can't flush here because this will be a secondary + // command buffer. + const_cast(engine).flush(); } } } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index a11d89e9c2..c48bd9be44 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -309,13 +310,6 @@ public: Command const* end() const noexcept { return mCommandEnd; } bool empty() const noexcept { return begin() == end(); } - // Helper to execute all the commands generated by this RenderPass - static void execute(RenderPass const& pass, - FEngine& engine, const char* name, - backend::Handle renderTarget, - backend::RenderPassParams params) noexcept; - - class BufferObjectHandleDeleter { std::reference_wrapper driver; public: @@ -364,7 +358,8 @@ public: Executor(RenderPass const& pass, Command const* b, Command const* e) noexcept; - void execute(FEngine& engine, const Command* first, const Command* last) const noexcept; + void execute(FEngine const& engine, backend::DriverApi& driver, + Command const* first, Command const* last) const noexcept; static backend::Viewport applyScissorViewport( backend::Viewport const& scissorViewport, @@ -389,7 +384,7 @@ public: void overrideScissor(backend::Viewport const& scissor) noexcept; - void execute(FEngine& engine, const char* name) const noexcept; + void execute(FEngine const& engine, backend::DriverApi& driver) const noexcept; }; // returns a new executor for this pass @@ -404,11 +399,12 @@ public: private: friend class FRenderer; friend class RenderPassBuilder; - RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept; + RenderPass(FEngine const& engine, backend::DriverApi& driver, + RenderPassBuilder const& builder) noexcept; // This is the main function of this class, this appends commands to the pass using // the current camera, geometry and flags set. This can be called multiple times if needed. - void appendCommands(FEngine& engine, + void appendCommands(FEngine const& engine, utils::Slice commands, utils::Range visibleRenderables, CommandTypeFlags commandTypeFlags, @@ -430,7 +426,8 @@ private: Command* begin, Command* end) noexcept; // instanceify commands then trims sentinels - RenderPass::Command* instanceify(FEngine& engine, + RenderPass::Command* instanceify(backend::DriverApi& driver, + backend::DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle, Command* begin, Command* end, int32_t eyeCount) const noexcept; @@ -470,8 +467,7 @@ private: mutable BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives mutable DescriptorSetSharedHandle mInstancedDescriptorSetHandle; // a descriptor-set to hold the ubo // a vector for our custom commands - using CustomCommandVector = std::vector>; + using CustomCommandVector = utils::FixedCapacityVector; mutable CustomCommandVector mCustomCommands; }; @@ -497,11 +493,10 @@ class RenderPassBuilder { uint32_t, RenderPass::Executor::CustomCommandFn>; - using CustomCommandContainer = std::vector>; + using CustomCommandContainer = std::vector; // we make this optional because it's not used often, and we don't want to have - // to construct it by default. + // to construct it by default. For the same reason we use a std::vector<> std::optional mCustomCommands; public: @@ -569,14 +564,14 @@ public: return *this; } - RenderPassBuilder& customCommand(FEngine& engine, + RenderPassBuilder& customCommand( uint8_t channel, RenderPass::Pass pass, RenderPass::CustomCommand custom, uint32_t order, const RenderPass::Executor::CustomCommandFn& command); - RenderPass build(FEngine& engine); + RenderPass build(FEngine const& engine, backend::DriverApi& driver) const; }; diff --git a/filament/src/RendererUtils.cpp b/filament/src/RendererUtils.cpp index 2e57af691f..5b671dda97 100644 --- a/filament/src/RendererUtils.cpp +++ b/filament/src/RendererUtils.cpp @@ -251,7 +251,7 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass( } driver.beginRenderPass(out.target, out.params); - passExecutor.execute(engine, resources.getPassName()); + passExecutor.execute(engine, driver); driver.endRenderPass(); // color pass is typically heavy, and we don't have much CPU work left after diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index fd5d314580..35fa104dcd 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -1361,8 +1361,8 @@ math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) // ------------------------------------------------------------------------------------------------ void ShadowMap::prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& cameraInfo) noexcept { - ShadowMapDescriptorSet::prepareCamera(transaction, engine, cameraInfo); + backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept { + ShadowMapDescriptorSet::prepareCamera(transaction, driver, cameraInfo); ShadowMapDescriptorSet::prepareLodBias(transaction, 0.0f); } @@ -1372,7 +1372,7 @@ void ShadowMap::prepareViewport(Transaction const& transaction, } void ShadowMap::prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept { + FEngine const& engine, math::float4 const& userTime) noexcept { ShadowMapDescriptorSet::prepareTime(transaction, engine, userTime); } diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index ed113f0a3a..cf131d4f64 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -193,11 +193,11 @@ public: using Transaction = ShadowMapDescriptorSet::Transaction; static void prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& cameraInfo) noexcept; + backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept; static void prepareViewport(Transaction const& transaction, backend::Viewport const& viewport) noexcept; static void prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept; + FEngine const& engine, math::float4 const& userTime) noexcept; static void prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept; static ShadowMapDescriptorSet::Transaction open(backend::DriverApi& driver) noexcept; diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 763e026db3..158259bdcf 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -314,9 +314,10 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG // "read" from one of its resource (only writes), so the FrameGraph culls it. builder.sideEffect(); }, - [this, &engine, &view, vsmShadowOptions, - scene, mainCameraInfo, userTime, passBuilder = passBuilder]( - FrameGraphResources const&, auto const& data, DriverApi& driver) mutable { + [=, passBuilder = passBuilder, + &engine = const_cast(engine), // FIXME: we want this const + &view = const_cast(view)] + (FrameGraphResources const&, auto const& data, DriverApi& driver) mutable { // Note: we could almost parallel_for the loop below, the problem currently is // that updatePrimitivesLod() updates temporary global state. @@ -363,7 +364,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo }; auto transaction = ShadowMap::open(driver); - ShadowMap::prepareCamera(transaction, engine, cameraInfo); + ShadowMap::prepareCamera(transaction, driver, cameraInfo); ShadowMap::prepareViewport(transaction, shadowMap.getViewport()); ShadowMap::prepareTime(transaction, engine, userTime); ShadowMap::prepareShadowMapping(transaction, @@ -371,8 +372,8 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG shadowMap.commit(transaction, engine, driver); // updatePrimitivesLod must be run before RenderPass::appendCommands. - view.updatePrimitivesLod(engine, - cameraInfo, scene->getRenderableData(), entry.range); + FView::updatePrimitivesLod(scene->getRenderableData(), + engine, cameraInfo, entry.range); // generate and sort the commands for rendering the shadow map @@ -394,7 +395,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG .visibilityMask(entry.visibilityMask) .geometry(scene->getRenderableData(), entry.range) .commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW) - .build(engine); + .build(engine, driver); entry.executor = pass.getExecutor(); @@ -525,7 +526,7 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG if (entry.shadowMap->hasVisibleShadows()) { entry.shadowMap->bind(driver); entry.executor.overrideScissor(entry.shadowMap->getScissor()); - entry.executor.execute(engine, "Shadow Pass"); + entry.executor.execute(engine, driver); } driver.endRenderPass(); }); @@ -791,9 +792,10 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin } } -void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap, FEngine& engine, FView& view, +void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap, + FEngine const& engine, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept { + FScene::LightSoa const& lightData) noexcept { auto& lcm = engine.getLightManager(); const size_t lightIndex = shadowMap.getLightIndex(); @@ -881,23 +883,23 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap, } } -void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView& view, +void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept { + FScene::LightSoa const& lightData) noexcept { - const uint8_t face = shadowMap.getFace(); - const size_t lightIndex = shadowMap.getLightIndex(); + uint8_t const face = shadowMap.getFace(); + size_t const lightIndex = shadowMap.getLightIndex(); // compute the frustum for this light // for spotlights, we cull shadow casters first because we already know the frustum, // this will help us find better near/far plane later - const auto position = lightData.elementAt(lightIndex).xyz; - const auto radius = lightData.elementAt(lightIndex).w; + auto const position = lightData.elementAt(lightIndex).xyz; + auto const radius = lightData.elementAt(lightIndex).w; // compute shadow map frustum for culling - const mat4f Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position); - const mat4f Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius); - const Frustum frustum{ math::highPrecisionMultiply(Mp, Mv) }; + mat4f const Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position); + mat4f const Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius); + Frustum const frustum{ math::highPrecisionMultiply(Mp, Mv) }; // Cull shadow casters float3 const* worldAABBCenter = renderableData.data(); diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 0d4b68324d..258c27dd6a 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -165,17 +165,17 @@ private: FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept; static void cullSpotShadowMap(ShadowMap const& map, - FEngine& engine, FView& view, + FEngine const& engine, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept; + FScene::LightSoa const& lightData) noexcept; void preparePointShadowMap(ShadowMap& map, FEngine& engine, FView& view, CameraInfo const& mainCameraInfo, FScene::LightSoa& lightData) noexcept; - static void cullPointShadowMap(ShadowMap const& shadowMap, FView& view, + static void cullPointShadowMap(ShadowMap const& shadowMap, FView const& view, FScene::RenderableSoa& renderableData, utils::Range range, - FScene::LightSoa& lightData) noexcept; + FScene::LightSoa const& lightData) noexcept; static void updateSpotVisibilityMasks( uint8_t visibleLayers, @@ -253,8 +253,8 @@ private: return const_cast(this)->getCascadedShadowMap(); } - utils::Slice getSpotShadowMaps() noexcept { - ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES); + utils::Slice getSpotShadowMaps() const noexcept { + ShadowMap const* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES); return { p, mSpotShadowMapCount }; } }; diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 272bafd4db..712b103038 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -208,10 +208,18 @@ public: return mRenderableManager; } + FRenderableManager const& getRenderableManager() const noexcept { + return mRenderableManager; + } + FLightManager& getLightManager() noexcept { return mLightManager; } + FLightManager const& getLightManager() const noexcept { + return mLightManager; + } + FCameraManager& getCameraManager() noexcept { return mCameraManager; } @@ -423,8 +431,11 @@ public: bool execute(); - utils::JobSystem& getJobSystem() noexcept { - return mJobSystem; + utils::JobSystem& getJobSystem() const noexcept { + // JobSystem is thread-safe, and it's always okay to return a non-const one, + // it's conceptually the same as if we were holding a non-const reference, as opposed + // to by-value class attribute. + return const_cast(mJobSystem); } std::default_random_engine& getRandomEngine() { diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 90f65b6401..6f39b21773 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -346,8 +346,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder, FMaterial::~FMaterial() noexcept = default; void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept { + DriverApi& driverApi = mEngine.getDriverApi(); if (mMaterialDomain == MaterialDomain::SURFACE) { - DriverApi& driverApi = mEngine.getDriverApi(); auto& cachedPrograms = mCachedPrograms; for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) { Variant const variant(k); @@ -377,7 +377,6 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV } } } else if (mMaterialDomain == MaterialDomain::POST_PROCESS) { - DriverApi& driverApi = mEngine.getDriverApi(); auto& cachedPrograms = mCachedPrograms; for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) { if ((k & variantMask) == variantValue) { diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index 02fe8b70f4..296fe60602 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -921,8 +921,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { // updatePrimitivesLod must be run before appendCommands and once for each set // of RenderPass::setCamera / RenderPass::setGeometry calls. - view.updatePrimitivesLod(engine, cameraInfo, - scene.getRenderableData(), view.getVisibleRenderables()); + FView::updatePrimitivesLod(scene.getRenderableData(), + engine, cameraInfo, view.getVisibleRenderables()); passBuilder.camera(cameraInfo); passBuilder.geometry(scene.getRenderableData(), view.getVisibleRenderables()); @@ -1025,8 +1025,10 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { passBuilder.variant(pickingVariant); passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::DEPTH); - RenderPass const pass{ passBuilder.build(mEngine) }; - RenderPass::execute(pass, mEngine, resources.getPassName(), out.target, out.params); + RenderPass const pass{ passBuilder.build(mEngine, driver) }; + driver.beginRenderPass(out.target, out.params); + pass.getExecutor().execute(mEngine, driver); + driver.endRenderPass(); }); picking = pickingRenderPass->picking; } @@ -1130,7 +1132,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { if (colorGradingConfigForColor.asSubpass) { // append color grading subpass after all other passes - passBuilder.customCommand(engine, 3, + passBuilder.customCommand(3, RenderPass::Pass::BLENDED, RenderPass::CustomCommand::EPILOG, 0, [&ppm, &driver, colorGradingConfigForColor]() { @@ -1138,7 +1140,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { }); } else if (colorGradingConfig.customResolve) { // append custom resolve subpass after all other passes - passBuilder.customCommand(engine, 3, + passBuilder.customCommand(3, RenderPass::Pass::BLENDED, RenderPass::CustomCommand::EPILOG, 0, [&ppm, &driver]() { @@ -1156,7 +1158,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) { passBuilder.renderFlags(renderFlags); } - RenderPass const pass{ passBuilder.build(engine) }; + RenderPass const pass{ passBuilder.build(engine, driver) }; FrameGraphTexture::Descriptor colorBufferDesc = { .width = config.physicalViewport.width, diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index a50b05c8e4..e7414f2a19 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -1065,8 +1065,8 @@ inline void FView::computeLightCameraDistances( } } -void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&, - FScene::RenderableSoa& renderableData, Range visible) noexcept { +void FView::updatePrimitivesLod(FScene::RenderableSoa& renderableData, + FEngine const& engine, CameraInfo const&, Range visible) noexcept { FRenderableManager const& rcm = engine.getRenderableManager(); for (uint32_t const index : visible) { uint8_t const level = 0; // TODO: pick the proper level of detail diff --git a/filament/src/details/View.h b/filament/src/details/View.h index ff61732a68..e8ffafd0f9 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -203,9 +203,9 @@ public: CameraInfo const& cameraInfo, math::float4 const& userTime, RenderPassBuilder const& passBuilder) noexcept; - void updatePrimitivesLod( - FEngine& engine, const CameraInfo& camera, - FScene::RenderableSoa& renderableData, Range visible) noexcept; + static void updatePrimitivesLod(FScene::RenderableSoa& renderableData, + FEngine const& engine, CameraInfo const& camera, + Range visible) noexcept; void setShadowingEnabled(bool enabled) noexcept { mShadowingEnabled = enabled; } diff --git a/filament/src/ds/ShadowMapDescriptorSet.cpp b/filament/src/ds/ShadowMapDescriptorSet.cpp index ae6aacaaed..da7f1c777c 100644 --- a/filament/src/ds/ShadowMapDescriptorSet.cpp +++ b/filament/src/ds/ShadowMapDescriptorSet.cpp @@ -61,7 +61,7 @@ PerViewUib& ShadowMapDescriptorSet::edit(Transaction const& transaction) noexcep } void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& camera) noexcept { + backend::DriverApi& driver, const CameraInfo& camera) noexcept { mat4f const& viewFromWorld = camera.view; mat4f const& worldFromView = camera.model; mat4f const& clipFromView = camera.projection; @@ -85,7 +85,7 @@ void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction, // with a clip-space of [-w, w] ==> z' = -z // with a clip-space of [0, w] ==> z' = (w - z)/2 - s.clipControl = engine.getDriverApi().getClipSpaceParams(); + s.clipControl = driver.getClipSpaceParams(); } void ShadowMapDescriptorSet::prepareLodBias(Transaction const& transaction, float bias) noexcept { @@ -103,7 +103,7 @@ void ShadowMapDescriptorSet::prepareViewport(Transaction const& transaction, } void ShadowMapDescriptorSet::prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept { + FEngine const& engine, math::float4 const& userTime) noexcept { auto& s = edit(transaction); const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1'000'000'000; const float fraction = float(double(oneSecondRemainder) / 1'000'000'000.0); diff --git a/filament/src/ds/ShadowMapDescriptorSet.h b/filament/src/ds/ShadowMapDescriptorSet.h index c1f55c5c89..e280330be0 100644 --- a/filament/src/ds/ShadowMapDescriptorSet.h +++ b/filament/src/ds/ShadowMapDescriptorSet.h @@ -56,7 +56,7 @@ public: void terminate(backend::DriverApi& driver); static void prepareCamera(Transaction const& transaction, - FEngine& engine, const CameraInfo& camera) noexcept; + backend::DriverApi& driver, const CameraInfo& camera) noexcept; static void prepareLodBias(Transaction const& transaction, float bias) noexcept; @@ -65,7 +65,7 @@ public: backend::Viewport const& viewport) noexcept; static void prepareTime(Transaction const& transaction, - FEngine& engine, math::float4 const& userTime) noexcept; + FEngine const& engine, math::float4 const& userTime) noexcept; static void prepareShadowMapping(Transaction const& transaction, bool highPrecision) noexcept;