| // Copyright 2018 The Dawn Authors | |
| // | |
| // 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 "dawn_native/vulkan/RenderPassCache.h" | |
| #include "common/BitSetIterator.h" | |
| #include "common/HashUtils.h" | |
| #include "dawn_native/vulkan/DeviceVk.h" | |
| #include "dawn_native/vulkan/TextureVk.h" | |
| #include "dawn_native/vulkan/VulkanError.h" | |
| namespace dawn_native { namespace vulkan { | |
| namespace { | |
| VkAttachmentLoadOp VulkanAttachmentLoadOp(wgpu::LoadOp op) { | |
| switch (op) { | |
| case wgpu::LoadOp::Load: | |
| return VK_ATTACHMENT_LOAD_OP_LOAD; | |
| case wgpu::LoadOp::Clear: | |
| return VK_ATTACHMENT_LOAD_OP_CLEAR; | |
| } | |
| UNREACHABLE(); | |
| } | |
| VkAttachmentStoreOp VulkanAttachmentStoreOp(wgpu::StoreOp op) { | |
| switch (op) { | |
| case wgpu::StoreOp::Store: | |
| return VK_ATTACHMENT_STORE_OP_STORE; | |
| case wgpu::StoreOp::Discard: | |
| return VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
| } | |
| UNREACHABLE(); | |
| } | |
| } // anonymous namespace | |
| // RenderPassCacheQuery | |
| void RenderPassCacheQuery::SetColor(ColorAttachmentIndex index, | |
| wgpu::TextureFormat format, | |
| wgpu::LoadOp loadOp, | |
| wgpu::StoreOp storeOp, | |
| bool hasResolveTarget) { | |
| colorMask.set(index); | |
| colorFormats[index] = format; | |
| colorLoadOp[index] = loadOp; | |
| colorStoreOp[index] = storeOp; | |
| resolveTargetMask[index] = hasResolveTarget; | |
| } | |
| void RenderPassCacheQuery::SetDepthStencil(wgpu::TextureFormat format, | |
| wgpu::LoadOp depthLoadOpIn, | |
| wgpu::StoreOp depthStoreOpIn, | |
| wgpu::LoadOp stencilLoadOpIn, | |
| wgpu::StoreOp stencilStoreOpIn) { | |
| hasDepthStencil = true; | |
| depthStencilFormat = format; | |
| depthLoadOp = depthLoadOpIn; | |
| depthStoreOp = depthStoreOpIn; | |
| stencilLoadOp = stencilLoadOpIn; | |
| stencilStoreOp = stencilStoreOpIn; | |
| } | |
| void RenderPassCacheQuery::SetSampleCount(uint32_t sampleCount) { | |
| this->sampleCount = sampleCount; | |
| } | |
| // RenderPassCache | |
| RenderPassCache::RenderPassCache(Device* device) : mDevice(device) { | |
| } | |
| RenderPassCache::~RenderPassCache() { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| for (auto it : mCache) { | |
| mDevice->fn.DestroyRenderPass(mDevice->GetVkDevice(), it.second, nullptr); | |
| } | |
| mCache.clear(); | |
| } | |
| ResultOrError<VkRenderPass> RenderPassCache::GetRenderPass(const RenderPassCacheQuery& query) { | |
| std::lock_guard<std::mutex> lock(mMutex); | |
| auto it = mCache.find(query); | |
| if (it != mCache.end()) { | |
| return VkRenderPass(it->second); | |
| } | |
| VkRenderPass renderPass; | |
| DAWN_TRY_ASSIGN(renderPass, CreateRenderPassForQuery(query)); | |
| mCache.emplace(query, renderPass); | |
| return renderPass; | |
| } | |
| ResultOrError<VkRenderPass> RenderPassCache::CreateRenderPassForQuery( | |
| const RenderPassCacheQuery& query) const { | |
| // The Vulkan subpasses want to know the layout of the attachments with VkAttachmentRef. | |
| // Precompute them as they must be pointer-chained in VkSubpassDescription | |
| std::array<VkAttachmentReference, kMaxColorAttachments> colorAttachmentRefs; | |
| std::array<VkAttachmentReference, kMaxColorAttachments> resolveAttachmentRefs; | |
| VkAttachmentReference depthStencilAttachmentRef; | |
| // Contains the attachment description that will be chained in the create info | |
| // The order of all attachments in attachmentDescs is "color-depthstencil-resolve". | |
| constexpr uint8_t kMaxAttachmentCount = kMaxColorAttachments * 2 + 1; | |
| std::array<VkAttachmentDescription, kMaxAttachmentCount> attachmentDescs = {}; | |
| VkSampleCountFlagBits vkSampleCount = VulkanSampleCount(query.sampleCount); | |
| uint32_t colorAttachmentIndex = 0; | |
| for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) { | |
| auto& attachmentRef = colorAttachmentRefs[colorAttachmentIndex]; | |
| auto& attachmentDesc = attachmentDescs[colorAttachmentIndex]; | |
| attachmentRef.attachment = colorAttachmentIndex; | |
| attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| attachmentDesc.flags = 0; | |
| attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); | |
| attachmentDesc.samples = vkSampleCount; | |
| attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.colorLoadOp[i]); | |
| attachmentDesc.storeOp = VulkanAttachmentStoreOp(query.colorStoreOp[i]); | |
| attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| ++colorAttachmentIndex; | |
| } | |
| uint32_t attachmentCount = colorAttachmentIndex; | |
| VkAttachmentReference* depthStencilAttachment = nullptr; | |
| if (query.hasDepthStencil) { | |
| auto& attachmentDesc = attachmentDescs[attachmentCount]; | |
| depthStencilAttachment = &depthStencilAttachmentRef; | |
| depthStencilAttachmentRef.attachment = attachmentCount; | |
| depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; | |
| attachmentDesc.flags = 0; | |
| attachmentDesc.format = VulkanImageFormat(mDevice, query.depthStencilFormat); | |
| attachmentDesc.samples = vkSampleCount; | |
| attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.depthLoadOp); | |
| attachmentDesc.storeOp = VulkanAttachmentStoreOp(query.depthStoreOp); | |
| attachmentDesc.stencilLoadOp = VulkanAttachmentLoadOp(query.stencilLoadOp); | |
| attachmentDesc.stencilStoreOp = VulkanAttachmentStoreOp(query.stencilStoreOp); | |
| attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; | |
| attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; | |
| ++attachmentCount; | |
| } | |
| uint32_t resolveAttachmentIndex = 0; | |
| for (ColorAttachmentIndex i : IterateBitSet(query.resolveTargetMask)) { | |
| auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex]; | |
| auto& attachmentDesc = attachmentDescs[attachmentCount]; | |
| attachmentRef.attachment = attachmentCount; | |
| attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| attachmentDesc.flags = 0; | |
| attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); | |
| attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; | |
| attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
| attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; | |
| attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
| ++attachmentCount; | |
| ++resolveAttachmentIndex; | |
| } | |
| // All color attachments without a corresponding resolve attachment must be set to VK_ATTACHMENT_UNUSED | |
| for (; resolveAttachmentIndex < colorAttachmentIndex; resolveAttachmentIndex++) { | |
| auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex]; | |
| attachmentRef.attachment = VK_ATTACHMENT_UNUSED; | |
| attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // The Khronos Vulkan validation layer will complain if not set | |
| } | |
| VkAttachmentReference* resolveTargetAttachmentRefs = | |
| query.resolveTargetMask.any() ? resolveAttachmentRefs.data() : nullptr; | |
| // Create the VkSubpassDescription that will be chained in the VkRenderPassCreateInfo | |
| VkSubpassDescription subpassDesc; | |
| subpassDesc.flags = 0; | |
| subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | |
| subpassDesc.inputAttachmentCount = 0; | |
| subpassDesc.pInputAttachments = nullptr; | |
| subpassDesc.colorAttachmentCount = colorAttachmentIndex; | |
| subpassDesc.pColorAttachments = colorAttachmentRefs.data(); | |
| subpassDesc.pResolveAttachments = resolveTargetAttachmentRefs; | |
| subpassDesc.pDepthStencilAttachment = depthStencilAttachment; | |
| subpassDesc.preserveAttachmentCount = 0; | |
| subpassDesc.pPreserveAttachments = nullptr; | |
| // Chain everything in VkRenderPassCreateInfo | |
| VkRenderPassCreateInfo createInfo; | |
| createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; | |
| createInfo.pNext = nullptr; | |
| createInfo.flags = 0; | |
| createInfo.attachmentCount = attachmentCount; | |
| createInfo.pAttachments = attachmentDescs.data(); | |
| createInfo.subpassCount = 1; | |
| createInfo.pSubpasses = &subpassDesc; | |
| createInfo.dependencyCount = 0; | |
| createInfo.pDependencies = nullptr; | |
| // Create the render pass from the zillion parameters | |
| VkRenderPass renderPass; | |
| DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, | |
| nullptr, &*renderPass), | |
| "CreateRenderPass")); | |
| return renderPass; | |
| } | |
| // RenderPassCache | |
| size_t RenderPassCache::CacheFuncs::operator()(const RenderPassCacheQuery& query) const { | |
| size_t hash = Hash(query.colorMask); | |
| HashCombine(&hash, Hash(query.resolveTargetMask)); | |
| for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) { | |
| HashCombine(&hash, query.colorFormats[i], query.colorLoadOp[i]); | |
| } | |
| HashCombine(&hash, query.hasDepthStencil); | |
| if (query.hasDepthStencil) { | |
| HashCombine(&hash, query.depthStencilFormat, query.depthLoadOp, query.stencilLoadOp); | |
| } | |
| HashCombine(&hash, query.sampleCount); | |
| return hash; | |
| } | |
| bool RenderPassCache::CacheFuncs::operator()(const RenderPassCacheQuery& a, | |
| const RenderPassCacheQuery& b) const { | |
| if (a.colorMask != b.colorMask) { | |
| return false; | |
| } | |
| if (a.resolveTargetMask != b.resolveTargetMask) { | |
| return false; | |
| } | |
| if (a.sampleCount != b.sampleCount) { | |
| return false; | |
| } | |
| for (ColorAttachmentIndex i : IterateBitSet(a.colorMask)) { | |
| if ((a.colorFormats[i] != b.colorFormats[i]) || | |
| (a.colorLoadOp[i] != b.colorLoadOp[i])) { | |
| return false; | |
| } | |
| } | |
| if (a.hasDepthStencil != b.hasDepthStencil) { | |
| return false; | |
| } | |
| if (a.hasDepthStencil) { | |
| if ((a.depthStencilFormat != b.depthStencilFormat) || | |
| (a.depthLoadOp != b.depthLoadOp) || (a.stencilLoadOp != b.stencilLoadOp)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| }} // namespace dawn_native::vulkan |