1 // Copyright 2018 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/vulkan/RenderPassCache.h" 16 17 #include "common/BitSetIterator.h" 18 #include "common/HashUtils.h" 19 #include "dawn_native/vulkan/DeviceVk.h" 20 #include "dawn_native/vulkan/TextureVk.h" 21 #include "dawn_native/vulkan/VulkanError.h" 22 23 namespace dawn_native { namespace vulkan { 24 25 namespace { VulkanAttachmentLoadOp(wgpu::LoadOp op)26 VkAttachmentLoadOp VulkanAttachmentLoadOp(wgpu::LoadOp op) { 27 switch (op) { 28 case wgpu::LoadOp::Load: 29 return VK_ATTACHMENT_LOAD_OP_LOAD; 30 case wgpu::LoadOp::Clear: 31 return VK_ATTACHMENT_LOAD_OP_CLEAR; 32 } 33 } 34 } // anonymous namespace 35 36 // RenderPassCacheQuery 37 SetColor(ColorAttachmentIndex index,wgpu::TextureFormat format,wgpu::LoadOp loadOp,bool hasResolveTarget)38 void RenderPassCacheQuery::SetColor(ColorAttachmentIndex index, 39 wgpu::TextureFormat format, 40 wgpu::LoadOp loadOp, 41 bool hasResolveTarget) { 42 colorMask.set(index); 43 colorFormats[index] = format; 44 colorLoadOp[index] = loadOp; 45 resolveTargetMask[index] = hasResolveTarget; 46 } 47 SetDepthStencil(wgpu::TextureFormat format,wgpu::LoadOp depthLoadOp,wgpu::LoadOp stencilLoadOp)48 void RenderPassCacheQuery::SetDepthStencil(wgpu::TextureFormat format, 49 wgpu::LoadOp depthLoadOp, 50 wgpu::LoadOp stencilLoadOp) { 51 hasDepthStencil = true; 52 depthStencilFormat = format; 53 this->depthLoadOp = depthLoadOp; 54 this->stencilLoadOp = stencilLoadOp; 55 } 56 SetSampleCount(uint32_t sampleCount)57 void RenderPassCacheQuery::SetSampleCount(uint32_t sampleCount) { 58 this->sampleCount = sampleCount; 59 } 60 61 // RenderPassCache 62 RenderPassCache(Device * device)63 RenderPassCache::RenderPassCache(Device* device) : mDevice(device) { 64 } 65 ~RenderPassCache()66 RenderPassCache::~RenderPassCache() { 67 for (auto it : mCache) { 68 mDevice->fn.DestroyRenderPass(mDevice->GetVkDevice(), it.second, nullptr); 69 } 70 mCache.clear(); 71 } 72 GetRenderPass(const RenderPassCacheQuery & query)73 ResultOrError<VkRenderPass> RenderPassCache::GetRenderPass(const RenderPassCacheQuery& query) { 74 auto it = mCache.find(query); 75 if (it != mCache.end()) { 76 return VkRenderPass(it->second); 77 } 78 79 VkRenderPass renderPass; 80 DAWN_TRY_ASSIGN(renderPass, CreateRenderPassForQuery(query)); 81 mCache.emplace(query, renderPass); 82 return renderPass; 83 } 84 CreateRenderPassForQuery(const RenderPassCacheQuery & query) const85 ResultOrError<VkRenderPass> RenderPassCache::CreateRenderPassForQuery( 86 const RenderPassCacheQuery& query) const { 87 // The Vulkan subpasses want to know the layout of the attachments with VkAttachmentRef. 88 // Precompute them as they must be pointer-chained in VkSubpassDescription 89 std::array<VkAttachmentReference, kMaxColorAttachments> colorAttachmentRefs; 90 std::array<VkAttachmentReference, kMaxColorAttachments> resolveAttachmentRefs; 91 VkAttachmentReference depthStencilAttachmentRef; 92 93 // Contains the attachment description that will be chained in the create info 94 // The order of all attachments in attachmentDescs is "color-depthstencil-resolve". 95 constexpr uint8_t kMaxAttachmentCount = kMaxColorAttachments * 2 + 1; 96 std::array<VkAttachmentDescription, kMaxAttachmentCount> attachmentDescs = {}; 97 98 VkSampleCountFlagBits vkSampleCount = VulkanSampleCount(query.sampleCount); 99 100 uint32_t colorAttachmentIndex = 0; 101 for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) { 102 auto& attachmentRef = colorAttachmentRefs[colorAttachmentIndex]; 103 auto& attachmentDesc = attachmentDescs[colorAttachmentIndex]; 104 105 attachmentRef.attachment = colorAttachmentIndex; 106 attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 107 108 attachmentDesc.flags = 0; 109 attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); 110 attachmentDesc.samples = vkSampleCount; 111 attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.colorLoadOp[i]); 112 attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 113 attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 114 attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 115 116 ++colorAttachmentIndex; 117 } 118 119 uint32_t attachmentCount = colorAttachmentIndex; 120 VkAttachmentReference* depthStencilAttachment = nullptr; 121 if (query.hasDepthStencil) { 122 auto& attachmentDesc = attachmentDescs[attachmentCount]; 123 124 depthStencilAttachment = &depthStencilAttachmentRef; 125 126 depthStencilAttachmentRef.attachment = attachmentCount; 127 depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 128 129 attachmentDesc.flags = 0; 130 attachmentDesc.format = VulkanImageFormat(mDevice, query.depthStencilFormat); 131 attachmentDesc.samples = vkSampleCount; 132 attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.depthLoadOp); 133 attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 134 attachmentDesc.stencilLoadOp = VulkanAttachmentLoadOp(query.stencilLoadOp); 135 attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; 136 attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 137 attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 138 139 ++attachmentCount; 140 } 141 142 uint32_t resolveAttachmentIndex = 0; 143 for (ColorAttachmentIndex i : IterateBitSet(query.resolveTargetMask)) { 144 auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex]; 145 auto& attachmentDesc = attachmentDescs[attachmentCount]; 146 147 attachmentRef.attachment = attachmentCount; 148 attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 149 150 attachmentDesc.flags = 0; 151 attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); 152 attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; 153 attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 154 attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 155 attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 156 attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 157 158 ++attachmentCount; 159 ++resolveAttachmentIndex; 160 } 161 162 // All color attachments without a corresponding resolve attachment must be set to VK_ATTACHMENT_UNUSED 163 for (; resolveAttachmentIndex < colorAttachmentIndex; resolveAttachmentIndex++) { 164 auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex]; 165 attachmentRef.attachment = VK_ATTACHMENT_UNUSED; 166 attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // The Khronos Vulkan validation layer will complain if not set 167 } 168 169 VkAttachmentReference* resolveTargetAttachmentRefs = 170 query.resolveTargetMask.any() ? resolveAttachmentRefs.data() : nullptr; 171 172 // Create the VkSubpassDescription that will be chained in the VkRenderPassCreateInfo 173 VkSubpassDescription subpassDesc; 174 subpassDesc.flags = 0; 175 subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 176 subpassDesc.inputAttachmentCount = 0; 177 subpassDesc.pInputAttachments = nullptr; 178 subpassDesc.colorAttachmentCount = colorAttachmentIndex; 179 subpassDesc.pColorAttachments = colorAttachmentRefs.data(); 180 subpassDesc.pResolveAttachments = resolveTargetAttachmentRefs; 181 subpassDesc.pDepthStencilAttachment = depthStencilAttachment; 182 subpassDesc.preserveAttachmentCount = 0; 183 subpassDesc.pPreserveAttachments = nullptr; 184 185 // Chain everything in VkRenderPassCreateInfo 186 VkRenderPassCreateInfo createInfo; 187 createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; 188 createInfo.pNext = nullptr; 189 createInfo.flags = 0; 190 createInfo.attachmentCount = attachmentCount; 191 createInfo.pAttachments = attachmentDescs.data(); 192 createInfo.subpassCount = 1; 193 createInfo.pSubpasses = &subpassDesc; 194 createInfo.dependencyCount = 0; 195 createInfo.pDependencies = nullptr; 196 197 // Create the render pass from the zillion parameters 198 VkRenderPass renderPass; 199 DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, 200 nullptr, &*renderPass), 201 "CreateRenderPass")); 202 return renderPass; 203 } 204 205 // RenderPassCache 206 operator ()(const RenderPassCacheQuery & query) const207 size_t RenderPassCache::CacheFuncs::operator()(const RenderPassCacheQuery& query) const { 208 size_t hash = Hash(query.colorMask); 209 210 HashCombine(&hash, Hash(query.resolveTargetMask)); 211 212 for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) { 213 HashCombine(&hash, query.colorFormats[i], query.colorLoadOp[i]); 214 } 215 216 HashCombine(&hash, query.hasDepthStencil); 217 if (query.hasDepthStencil) { 218 HashCombine(&hash, query.depthStencilFormat, query.depthLoadOp, query.stencilLoadOp); 219 } 220 221 HashCombine(&hash, query.sampleCount); 222 223 return hash; 224 } 225 operator ()(const RenderPassCacheQuery & a,const RenderPassCacheQuery & b) const226 bool RenderPassCache::CacheFuncs::operator()(const RenderPassCacheQuery& a, 227 const RenderPassCacheQuery& b) const { 228 if (a.colorMask != b.colorMask) { 229 return false; 230 } 231 232 if (a.resolveTargetMask != b.resolveTargetMask) { 233 return false; 234 } 235 236 if (a.sampleCount != b.sampleCount) { 237 return false; 238 } 239 240 for (ColorAttachmentIndex i : IterateBitSet(a.colorMask)) { 241 if ((a.colorFormats[i] != b.colorFormats[i]) || 242 (a.colorLoadOp[i] != b.colorLoadOp[i])) { 243 return false; 244 } 245 } 246 247 if (a.hasDepthStencil != b.hasDepthStencil) { 248 return false; 249 } 250 251 if (a.hasDepthStencil) { 252 if ((a.depthStencilFormat != b.depthStencilFormat) || 253 (a.depthLoadOp != b.depthLoadOp) || (a.stencilLoadOp != b.stencilLoadOp)) { 254 return false; 255 } 256 } 257 258 return true; 259 } 260 }} // namespace dawn_native::vulkan 261