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