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 UNREACHABLE(); 34 } 35 VulkanAttachmentStoreOp(wgpu::StoreOp op)36 VkAttachmentStoreOp VulkanAttachmentStoreOp(wgpu::StoreOp op) { 37 // TODO(crbug.com/dawn/485): return STORE_OP_STORE_NONE_QCOM if the device has required 38 // extension. 39 switch (op) { 40 case wgpu::StoreOp::Store: 41 return VK_ATTACHMENT_STORE_OP_STORE; 42 case wgpu::StoreOp::Discard: 43 return VK_ATTACHMENT_STORE_OP_DONT_CARE; 44 } 45 UNREACHABLE(); 46 } 47 } // anonymous namespace 48 49 // RenderPassCacheQuery 50 SetColor(ColorAttachmentIndex index,wgpu::TextureFormat format,wgpu::LoadOp loadOp,wgpu::StoreOp storeOp,bool hasResolveTarget)51 void RenderPassCacheQuery::SetColor(ColorAttachmentIndex index, 52 wgpu::TextureFormat format, 53 wgpu::LoadOp loadOp, 54 wgpu::StoreOp storeOp, 55 bool hasResolveTarget) { 56 colorMask.set(index); 57 colorFormats[index] = format; 58 colorLoadOp[index] = loadOp; 59 colorStoreOp[index] = storeOp; 60 resolveTargetMask[index] = hasResolveTarget; 61 } 62 SetDepthStencil(wgpu::TextureFormat format,wgpu::LoadOp depthLoadOpIn,wgpu::StoreOp depthStoreOpIn,wgpu::LoadOp stencilLoadOpIn,wgpu::StoreOp stencilStoreOpIn,bool readOnly)63 void RenderPassCacheQuery::SetDepthStencil(wgpu::TextureFormat format, 64 wgpu::LoadOp depthLoadOpIn, 65 wgpu::StoreOp depthStoreOpIn, 66 wgpu::LoadOp stencilLoadOpIn, 67 wgpu::StoreOp stencilStoreOpIn, 68 bool readOnly) { 69 hasDepthStencil = true; 70 depthStencilFormat = format; 71 depthLoadOp = depthLoadOpIn; 72 depthStoreOp = depthStoreOpIn; 73 stencilLoadOp = stencilLoadOpIn; 74 stencilStoreOp = stencilStoreOpIn; 75 readOnlyDepthStencil = readOnly; 76 } 77 SetSampleCount(uint32_t sampleCount)78 void RenderPassCacheQuery::SetSampleCount(uint32_t sampleCount) { 79 this->sampleCount = sampleCount; 80 } 81 82 // RenderPassCache 83 RenderPassCache(Device * device)84 RenderPassCache::RenderPassCache(Device* device) : mDevice(device) { 85 } 86 ~RenderPassCache()87 RenderPassCache::~RenderPassCache() { 88 std::lock_guard<std::mutex> lock(mMutex); 89 for (auto it : mCache) { 90 mDevice->fn.DestroyRenderPass(mDevice->GetVkDevice(), it.second, nullptr); 91 } 92 93 mCache.clear(); 94 } 95 GetRenderPass(const RenderPassCacheQuery & query)96 ResultOrError<VkRenderPass> RenderPassCache::GetRenderPass(const RenderPassCacheQuery& query) { 97 std::lock_guard<std::mutex> lock(mMutex); 98 auto it = mCache.find(query); 99 if (it != mCache.end()) { 100 return VkRenderPass(it->second); 101 } 102 103 VkRenderPass renderPass; 104 DAWN_TRY_ASSIGN(renderPass, CreateRenderPassForQuery(query)); 105 mCache.emplace(query, renderPass); 106 return renderPass; 107 } 108 CreateRenderPassForQuery(const RenderPassCacheQuery & query) const109 ResultOrError<VkRenderPass> RenderPassCache::CreateRenderPassForQuery( 110 const RenderPassCacheQuery& query) const { 111 // The Vulkan subpasses want to know the layout of the attachments with VkAttachmentRef. 112 // Precompute them as they must be pointer-chained in VkSubpassDescription 113 std::array<VkAttachmentReference, kMaxColorAttachments> colorAttachmentRefs; 114 std::array<VkAttachmentReference, kMaxColorAttachments> resolveAttachmentRefs; 115 VkAttachmentReference depthStencilAttachmentRef; 116 117 // Contains the attachment description that will be chained in the create info 118 // The order of all attachments in attachmentDescs is "color-depthstencil-resolve". 119 constexpr uint8_t kMaxAttachmentCount = kMaxColorAttachments * 2 + 1; 120 std::array<VkAttachmentDescription, kMaxAttachmentCount> attachmentDescs = {}; 121 122 VkSampleCountFlagBits vkSampleCount = VulkanSampleCount(query.sampleCount); 123 124 uint32_t colorAttachmentIndex = 0; 125 for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) { 126 auto& attachmentRef = colorAttachmentRefs[colorAttachmentIndex]; 127 auto& attachmentDesc = attachmentDescs[colorAttachmentIndex]; 128 129 attachmentRef.attachment = colorAttachmentIndex; 130 attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 131 132 attachmentDesc.flags = 0; 133 attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); 134 attachmentDesc.samples = vkSampleCount; 135 attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.colorLoadOp[i]); 136 attachmentDesc.storeOp = VulkanAttachmentStoreOp(query.colorStoreOp[i]); 137 attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 138 attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 139 140 ++colorAttachmentIndex; 141 } 142 143 uint32_t attachmentCount = colorAttachmentIndex; 144 VkAttachmentReference* depthStencilAttachment = nullptr; 145 if (query.hasDepthStencil) { 146 auto& attachmentDesc = attachmentDescs[attachmentCount]; 147 148 depthStencilAttachment = &depthStencilAttachmentRef; 149 150 depthStencilAttachmentRef.attachment = attachmentCount; 151 depthStencilAttachmentRef.layout = 152 query.readOnlyDepthStencil ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL 153 : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 154 155 attachmentDesc.flags = 0; 156 attachmentDesc.format = VulkanImageFormat(mDevice, query.depthStencilFormat); 157 attachmentDesc.samples = vkSampleCount; 158 159 attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.depthLoadOp); 160 attachmentDesc.storeOp = VulkanAttachmentStoreOp(query.depthStoreOp); 161 attachmentDesc.stencilLoadOp = VulkanAttachmentLoadOp(query.stencilLoadOp); 162 attachmentDesc.stencilStoreOp = VulkanAttachmentStoreOp(query.stencilStoreOp); 163 164 // There is only one subpass, so it is safe to set both initialLayout and finalLayout to 165 // the only subpass's layout. 166 attachmentDesc.initialLayout = depthStencilAttachmentRef.layout; 167 attachmentDesc.finalLayout = depthStencilAttachmentRef.layout; 168 169 ++attachmentCount; 170 } 171 172 uint32_t resolveAttachmentIndex = 0; 173 for (ColorAttachmentIndex i : IterateBitSet(query.resolveTargetMask)) { 174 auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex]; 175 auto& attachmentDesc = attachmentDescs[attachmentCount]; 176 177 attachmentRef.attachment = attachmentCount; 178 attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 179 180 attachmentDesc.flags = 0; 181 attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); 182 attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; 183 attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 184 attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 185 attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 186 attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 187 188 ++attachmentCount; 189 ++resolveAttachmentIndex; 190 } 191 192 // All color attachments without a corresponding resolve attachment must be set to VK_ATTACHMENT_UNUSED 193 for (; resolveAttachmentIndex < colorAttachmentIndex; resolveAttachmentIndex++) { 194 auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex]; 195 attachmentRef.attachment = VK_ATTACHMENT_UNUSED; 196 attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; // The Khronos Vulkan validation layer will complain if not set 197 } 198 199 VkAttachmentReference* resolveTargetAttachmentRefs = 200 query.resolveTargetMask.any() ? resolveAttachmentRefs.data() : nullptr; 201 202 // Create the VkSubpassDescription that will be chained in the VkRenderPassCreateInfo 203 VkSubpassDescription subpassDesc; 204 subpassDesc.flags = 0; 205 subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 206 subpassDesc.inputAttachmentCount = 0; 207 subpassDesc.pInputAttachments = nullptr; 208 subpassDesc.colorAttachmentCount = colorAttachmentIndex; 209 subpassDesc.pColorAttachments = colorAttachmentRefs.data(); 210 subpassDesc.pResolveAttachments = resolveTargetAttachmentRefs; 211 subpassDesc.pDepthStencilAttachment = depthStencilAttachment; 212 subpassDesc.preserveAttachmentCount = 0; 213 subpassDesc.pPreserveAttachments = nullptr; 214 215 // Chain everything in VkRenderPassCreateInfo 216 VkRenderPassCreateInfo createInfo; 217 createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; 218 createInfo.pNext = nullptr; 219 createInfo.flags = 0; 220 createInfo.attachmentCount = attachmentCount; 221 createInfo.pAttachments = attachmentDescs.data(); 222 createInfo.subpassCount = 1; 223 createInfo.pSubpasses = &subpassDesc; 224 createInfo.dependencyCount = 0; 225 createInfo.pDependencies = nullptr; 226 227 // Create the render pass from the zillion parameters 228 VkRenderPass renderPass; 229 DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, 230 nullptr, &*renderPass), 231 "CreateRenderPass")); 232 return renderPass; 233 } 234 235 // RenderPassCache 236 operator ()(const RenderPassCacheQuery & query) const237 size_t RenderPassCache::CacheFuncs::operator()(const RenderPassCacheQuery& query) const { 238 size_t hash = Hash(query.colorMask); 239 240 HashCombine(&hash, Hash(query.resolveTargetMask)); 241 242 for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) { 243 HashCombine(&hash, query.colorFormats[i], query.colorLoadOp[i]); 244 } 245 246 HashCombine(&hash, query.hasDepthStencil); 247 if (query.hasDepthStencil) { 248 HashCombine(&hash, query.depthStencilFormat, query.depthLoadOp, query.stencilLoadOp, 249 query.readOnlyDepthStencil); 250 } 251 252 HashCombine(&hash, query.sampleCount); 253 254 return hash; 255 } 256 operator ()(const RenderPassCacheQuery & a,const RenderPassCacheQuery & b) const257 bool RenderPassCache::CacheFuncs::operator()(const RenderPassCacheQuery& a, 258 const RenderPassCacheQuery& b) const { 259 if (a.colorMask != b.colorMask) { 260 return false; 261 } 262 263 if (a.resolveTargetMask != b.resolveTargetMask) { 264 return false; 265 } 266 267 if (a.sampleCount != b.sampleCount) { 268 return false; 269 } 270 271 for (ColorAttachmentIndex i : IterateBitSet(a.colorMask)) { 272 if ((a.colorFormats[i] != b.colorFormats[i]) || 273 (a.colorLoadOp[i] != b.colorLoadOp[i])) { 274 return false; 275 } 276 } 277 278 if (a.hasDepthStencil != b.hasDepthStencil) { 279 return false; 280 } 281 282 if (a.hasDepthStencil) { 283 if ((a.depthStencilFormat != b.depthStencilFormat) || 284 (a.depthLoadOp != b.depthLoadOp) || (a.stencilLoadOp != b.stencilLoadOp) || 285 (a.readOnlyDepthStencil != b.readOnlyDepthStencil)) { 286 return false; 287 } 288 } 289 290 return true; 291 } 292 }} // namespace dawn_native::vulkan 293