• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
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 
16 #include "node_context_pool_manager_vk.h"
17 
18 #include <cstdint>
19 #include <vulkan/vulkan.h>
20 
21 #include <base/containers/fixed_string.h>
22 #include <base/math/mathf.h>
23 #include <base/util/compile_time_hashes.h>
24 #include <render/device/pipeline_state_desc.h>
25 #include <render/namespace.h>
26 
27 #include "device/device.h"
28 #include "device/gpu_image.h"
29 #include "device/gpu_resource_handle_util.h"
30 #include "device/gpu_resource_manager.h"
31 #include "nodecontext/node_context_pool_manager.h"
32 #include "nodecontext/render_command_list.h"
33 #include "util/log.h"
34 #include "vulkan/device_vk.h"
35 #include "vulkan/gpu_image_vk.h"
36 #include "vulkan/gpu_resource_util_vk.h"
37 #include "vulkan/pipeline_create_functions_vk.h"
38 #include "vulkan/validate_vk.h"
39 
40 using namespace BASE_NS;
41 
42 template<>
hash(const RENDER_NS::ImageLayout & val)43 uint64_t BASE_NS::hash(const RENDER_NS::ImageLayout& val)
44 {
45     return static_cast<uint64_t>(val);
46 }
47 template<>
hash(const RENDER_NS::RenderPassSubpassDesc & subpass)48 uint64_t BASE_NS::hash(const RENDER_NS::RenderPassSubpassDesc& subpass)
49 {
50     uint64_t seed = 0;
51     HashRange(seed, subpass.inputAttachmentIndices, subpass.inputAttachmentIndices + subpass.inputAttachmentCount);
52     HashRange(seed, subpass.colorAttachmentIndices, subpass.colorAttachmentIndices + subpass.colorAttachmentCount);
53     HashRange(
54         seed, subpass.resolveAttachmentIndices, subpass.resolveAttachmentIndices + subpass.resolveAttachmentCount);
55     if (subpass.depthAttachmentCount) {
56         HashCombine(seed, static_cast<uint64_t>(subpass.depthAttachmentIndex));
57     }
58     return seed;
59 }
60 
61 RENDER_BEGIN_NAMESPACE()
62 namespace {
63 struct FBSize {
64     uint32_t width { 0 };
65     uint32_t height { 0 };
66     uint32_t layers { 1 };
67 };
68 
HashRenderPassCompatibility(uint64_t & hash,const RenderPassDesc & renderPassDesc,const LowLevelRenderPassCompatibilityDescVk & renderPassCompatibilityDesc,const RenderPassSubpassDesc & subpasses)69 inline void HashRenderPassCompatibility(uint64_t& hash, const RenderPassDesc& renderPassDesc,
70     const LowLevelRenderPassCompatibilityDescVk& renderPassCompatibilityDesc, const RenderPassSubpassDesc& subpasses)
71 {
72     for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
73         const LowLevelRenderPassCompatibilityDescVk::Attachment& atCompatibilityDesc =
74             renderPassCompatibilityDesc.attachments[idx];
75         HashCombine(hash, static_cast<uint64_t>(atCompatibilityDesc.format),
76             static_cast<uint64_t>(atCompatibilityDesc.sampleCountFlags));
77     }
78     HashRange(hash, &subpasses, &subpasses + renderPassDesc.subpassCount);
79 }
80 
HashRenderPassLayouts(uint64_t & hash,const RenderPassDesc & renderPassDesc,const RenderPassImageLayouts & renderPassImageLayouts)81 inline void HashRenderPassLayouts(
82     uint64_t& hash, const RenderPassDesc& renderPassDesc, const RenderPassImageLayouts& renderPassImageLayouts)
83 {
84     for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
85         HashCombine(hash, renderPassImageLayouts.attachmentInitialLayouts[idx],
86             renderPassImageLayouts.attachmentFinalLayouts[idx]);
87     }
88 }
89 
HashFramebuffer(uint64_t & hash,const RenderPassDesc & renderPassDesc,const GpuResourceManager & gpuResourceMgr)90 inline void HashFramebuffer(
91     uint64_t& hash, const RenderPassDesc& renderPassDesc, const GpuResourceManager& gpuResourceMgr)
92 {
93     for (uint32_t idx = 0; idx < renderPassDesc.attachmentCount; ++idx) {
94         const RenderPassDesc::AttachmentDesc& atDesc = renderPassDesc.attachments[idx];
95         // with generation index
96         const EngineResourceHandle gpuHandle = gpuResourceMgr.GetGpuHandle(renderPassDesc.attachmentHandles[idx]);
97         HashCombine(hash, gpuHandle.id, static_cast<uint64_t>(atDesc.layer), static_cast<uint64_t>(atDesc.mipLevel));
98     }
99 }
100 
101 struct RenderPassHashes {
102     uint64_t renderPassCompatibilityHash { 0 };
103     uint64_t renderPassHash { 0 };  // continued hashing from compatibility
104     uint64_t frameBufferHash { 0 }; // only framebuffer related hash
105 };
106 
HashBeginRenderPass(const RenderCommandBeginRenderPass & beginRenderPass,const LowLevelRenderPassCompatibilityDescVk & renderPassCompatibilityDesc,const GpuResourceManager & gpuResourceMgr)107 inline RenderPassHashes HashBeginRenderPass(const RenderCommandBeginRenderPass& beginRenderPass,
108     const LowLevelRenderPassCompatibilityDescVk& renderPassCompatibilityDesc, const GpuResourceManager& gpuResourceMgr)
109 {
110     RenderPassHashes rpHashes;
111 
112     const auto& renderPassDesc = beginRenderPass.renderPassDesc;
113 
114     PLUGIN_ASSERT(renderPassDesc.subpassCount > 0);
115     HashRenderPassCompatibility(rpHashes.renderPassCompatibilityHash, renderPassDesc, renderPassCompatibilityDesc,
116         beginRenderPass.subpasses[0]);
117 
118     rpHashes.renderPassHash = rpHashes.renderPassCompatibilityHash; // for starting point
119     HashRenderPassLayouts(rpHashes.renderPassHash, renderPassDesc, beginRenderPass.imageLayouts);
120 
121     rpHashes.frameBufferHash = rpHashes.renderPassCompatibilityHash; // depends on the compatible render pass
122     HashFramebuffer(rpHashes.frameBufferHash, renderPassDesc, gpuResourceMgr);
123 
124     return rpHashes;
125 }
126 
CreateFramebuffer(const GpuResourceManager & gpuResourceMgr,const RenderPassDesc & renderPassDesc,const VkDevice device,const VkRenderPass compatibleRenderPass)127 VkFramebuffer CreateFramebuffer(const GpuResourceManager& gpuResourceMgr, const RenderPassDesc& renderPassDesc,
128     const VkDevice device, const VkRenderPass compatibleRenderPass)
129 {
130     const uint32_t attachmentCount = renderPassDesc.attachmentCount;
131     PLUGIN_ASSERT(attachmentCount <= PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT);
132 
133     FBSize size;
134     VkImageView imageViews[PipelineStateConstants::MAX_RENDER_PASS_ATTACHMENT_COUNT];
135     uint32_t viewIndex = 0;
136 
137     for (uint32_t idx = 0; idx < attachmentCount; ++idx) {
138         const RenderHandle handle = renderPassDesc.attachmentHandles[idx];
139         const RenderPassDesc::AttachmentDesc& attachmentDesc = renderPassDesc.attachments[idx];
140         const GpuImageVk* image = gpuResourceMgr.GetImage<GpuImageVk>(handle);
141         PLUGIN_ASSERT(image);
142         if (image) {
143             const GpuImageDesc& imageDesc = image->GetDesc();
144             size.width = imageDesc.width;
145             size.height = imageDesc.height;
146 
147             const GpuImagePlatformDataVk& plat = image->GetPlatformData();
148             const GpuImagePlatformDataViewsVk& imagePlat = image->GetPlatformDataViews();
149             imageViews[viewIndex] = plat.imageViewBase;
150             if ((attachmentDesc.mipLevel >= 1) && (attachmentDesc.mipLevel < imagePlat.mipImageViews.size())) {
151                 imageViews[viewIndex] = imagePlat.mipImageViews[attachmentDesc.mipLevel];
152                 size.width = Math::max(1u, size.width >> attachmentDesc.mipLevel);
153                 size.height = Math::max(1u, size.height >> attachmentDesc.mipLevel);
154             } else if ((attachmentDesc.layer >= 1) && (attachmentDesc.layer < imagePlat.layerImageViews.size())) {
155                 imageViews[viewIndex] = imagePlat.layerImageViews[attachmentDesc.layer];
156             }
157             viewIndex++;
158         }
159     }
160     PLUGIN_ASSERT(viewIndex == attachmentCount);
161 
162     const VkFramebufferCreateInfo framebufferCreateInfo {
163         VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // sType
164         nullptr,                                   // pNext
165         VkFramebufferCreateFlags { 0 },            // flags
166         compatibleRenderPass,                      // renderPass
167         attachmentCount,                           // attachmentCount
168         imageViews,                                // pAttachments
169         size.width,                                // width
170         size.height,                               // height
171         size.layers,                               // layers
172     };
173 
174     VkFramebuffer framebuffer = VK_NULL_HANDLE;
175     VALIDATE_VK_RESULT(vkCreateFramebuffer(device, // device
176         &framebufferCreateInfo,                    // pCreateInfo
177         nullptr,                                   // pAllocator
178         &framebuffer));                            // pFramebuffer
179 
180     return framebuffer;
181 }
182 } // namespace
183 
NodeContextPoolManagerVk(Device & device,GpuResourceManager & gpuResourceManager,const GpuQueue & gpuQueue)184 NodeContextPoolManagerVk::NodeContextPoolManagerVk(
185     Device& device, GpuResourceManager& gpuResourceManager, const GpuQueue& gpuQueue)
186     : NodeContextPoolManager(), device_ { device }, gpuResourceMgr_ { gpuResourceManager }
187 {
188     const DeviceVk& deviceVk = static_cast<const DeviceVk&>(device_);
189     const VkDevice vkDevice = static_cast<const DevicePlatformDataVk&>(device_.GetPlatformData()).device;
190 
191     const LowLevelGpuQueueVk lowLevelGpuQueue = deviceVk.GetGpuQueue(gpuQueue);
192 
193     const uint32_t bufferingCount = device_.GetCommandBufferingCount();
194     if (bufferingCount > 0) {
195         // prepare and create command buffers
196         commandPools_.resize(bufferingCount);
197 
198         constexpr VkCommandPoolCreateFlags commandPoolCreateFlags { 0u };
199         const uint32_t queueFamilyIndex = lowLevelGpuQueue.queueInfo.queueFamilyIndex;
200         const VkCommandPoolCreateInfo commandPoolCreateInfo {
201             VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // sType
202             nullptr,                                    // pNext
203             commandPoolCreateFlags,                     // flags
204             queueFamilyIndex,                           // queueFamilyIndexlayers
205         };
206         constexpr VkSemaphoreCreateFlags semaphoreCreateFlags { 0 };
207         constexpr VkSemaphoreCreateInfo semaphoreCreateInfo {
208             VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, // sType
209             nullptr,                                 // pNext
210             semaphoreCreateFlags,                    // flags
211         };
212 
213         for (uint32_t frameIdx = 0; frameIdx < commandPools_.size(); ++frameIdx) {
214             auto& cmdPool = commandPools_[frameIdx];
215             VALIDATE_VK_RESULT(vkCreateCommandPool(vkDevice, // device
216                 &commandPoolCreateInfo,                      // pCreateInfo
217                 nullptr,                                     // pAllocator
218                 &cmdPool.commandPool));                      // pCommandPool
219 
220             // pre-create command buffers and semaphores
221             constexpr VkCommandBufferLevel commandBufferLevel { VK_COMMAND_BUFFER_LEVEL_PRIMARY };
222             const VkCommandBufferAllocateInfo commandBufferAllocateInfo {
223                 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
224                 nullptr,                                        // pNext
225                 cmdPool.commandPool,                            // commandPool
226                 commandBufferLevel,                             // level
227                 1,                                              // commandBufferCount
228             };
229 
230             VALIDATE_VK_RESULT(vkAllocateCommandBuffers(vkDevice, // device
231                 &commandBufferAllocateInfo,                       // pAllocateInfo
232                 &cmdPool.commandBuffer.commandBuffer));           // pCommandBuffers
233 
234             VALIDATE_VK_RESULT(vkCreateSemaphore(vkDevice, // device
235                 &semaphoreCreateInfo,                      // pCreateInfo
236                 nullptr,                                   // pAllocator
237                 &cmdPool.commandBuffer.semaphore));        // pSemaphore
238 
239             // NOTE: cmd buffers taged in first beginFrame
240         }
241     }
242 }
243 
~NodeContextPoolManagerVk()244 NodeContextPoolManagerVk::~NodeContextPoolManagerVk()
245 {
246     const VkDevice device = ((const DevicePlatformDataVk&)device_.GetPlatformData()).device;
247 
248     for (auto& cmdPoolRef : commandPools_) {
249         vkDestroySemaphore(device,              // device
250             cmdPoolRef.commandBuffer.semaphore, // semaphore
251             nullptr);                           // pAllocator
252         vkDestroyCommandPool(device,            // device
253             cmdPoolRef.commandPool,             // commandPool
254             nullptr);                           // pAllocator
255     }
256 
257     for (auto& ref : framebufferCache_.hashToElement) {
258         if (ref.second.frameBuffer != VK_NULL_HANDLE) {
259             vkDestroyFramebuffer(device, // device
260                 ref.second.frameBuffer,  // framebuffer
261                 nullptr);                // pAllocator
262         }
263     }
264     for (auto& ref : renderPassCache_.hashToElement) {
265         if (ref.second.renderPass != VK_NULL_HANDLE) {
266             renderPassCreator_.DestroyRenderPass(device, ref.second.renderPass);
267         }
268     }
269     for (auto& ref : renderPassCompatibilityCache_.hashToElement) {
270         if (ref.second.renderPass != VK_NULL_HANDLE) {
271             renderPassCreator_.DestroyRenderPass(device, ref.second.renderPass);
272         }
273     }
274 }
275 
BeginFrame()276 void NodeContextPoolManagerVk::BeginFrame()
277 {
278 #if (RENDER_VULKAN_VALIDATION_ENABLED == 1)
279     if (firstFrame_) {
280         firstFrame_ = false;
281         for (const auto& cmdPoolRef : commandPools_) {
282             GpuResourceUtil::DebugObjectNameVk(device_, VK_OBJECT_TYPE_COMMAND_BUFFER,
283                 VulkanHandleCast<uint64_t>(cmdPoolRef.commandBuffer.commandBuffer), debugName_ + "_cmd_buf");
284         }
285     }
286 #endif
287 
288     bufferingIndex_ = (bufferingIndex_ + 1) % (uint32_t)commandPools_.size();
289 
290     constexpr uint64_t additionalFrameCount { 2u };
291     const auto minAge = device_.GetCommandBufferingCount() + additionalFrameCount;
292     const auto ageLimit = (device_.GetFrameCount() < minAge) ? 0 : (device_.GetFrameCount() - minAge);
293 
294     const VkDevice device = ((const DevicePlatformDataVk&)device_.GetPlatformData()).device;
295     {
296         auto& cache = framebufferCache_.hashToElement;
297         for (auto iter = cache.begin(); iter != cache.end();) {
298             if (iter->second.frameUseIndex < ageLimit) {
299                 PLUGIN_ASSERT(iter->second.frameBuffer != VK_NULL_HANDLE);
300                 vkDestroyFramebuffer(device, iter->second.frameBuffer, nullptr);
301                 iter = cache.erase(iter);
302             } else {
303                 ++iter;
304             }
305         }
306     }
307     {
308         auto& cache = renderPassCache_.hashToElement;
309         for (auto iter = cache.begin(); iter != cache.end();) {
310             if (iter->second.frameUseIndex < ageLimit) {
311                 PLUGIN_ASSERT(iter->second.renderPass != VK_NULL_HANDLE);
312                 renderPassCreator_.DestroyRenderPass(device, iter->second.renderPass);
313                 iter = cache.erase(iter);
314             } else {
315                 ++iter;
316             }
317         }
318     }
319 }
320 
GetContextCommandPool() const321 const ContextCommandPoolVk& NodeContextPoolManagerVk::GetContextCommandPool() const
322 {
323     return commandPools_[bufferingIndex_];
324 }
325 
GetRenderPassData(const RenderCommandBeginRenderPass & beginRenderPass)326 LowLevelRenderPassDataVk NodeContextPoolManagerVk::GetRenderPassData(
327     const RenderCommandBeginRenderPass& beginRenderPass)
328 {
329     LowLevelRenderPassDataVk renderPassData;
330     renderPassData.subpassIndex = beginRenderPass.subpassStartIndex;
331 
332     // collect render pass attachment compatibility info and default viewport/scissor
333     for (uint32_t idx = 0; idx < beginRenderPass.renderPassDesc.attachmentCount; ++idx) {
334         const GpuImage* image = gpuResourceMgr_.GetImage(beginRenderPass.renderPassDesc.attachmentHandles[idx]);
335         PLUGIN_ASSERT(image);
336         if (image) {
337             const auto& imageDesc = image->GetDesc();
338             renderPassData.renderPassCompatibilityDesc.attachments[idx] = { (VkFormat)imageDesc.format,
339                 (VkSampleCountFlagBits)imageDesc.sampleCountFlags };
340             if (idx == 0) {
341                 uint32_t maxFbWidth = imageDesc.width;
342                 uint32_t maxFbHeight = imageDesc.height;
343                 const auto& attachmentRef = beginRenderPass.renderPassDesc.attachments[idx];
344                 if ((attachmentRef.mipLevel >= 1) && (attachmentRef.mipLevel < imageDesc.mipCount)) {
345                     maxFbWidth = Math::max(1u, maxFbWidth >> attachmentRef.mipLevel);
346                     maxFbHeight = Math::max(1u, maxFbHeight >> attachmentRef.mipLevel);
347                 }
348                 renderPassData.viewport = { 0.0f, 0.0f, static_cast<float>(maxFbWidth), static_cast<float>(maxFbHeight),
349                     0.0f, 1.0f };
350                 renderPassData.scissor = { { 0, 0 }, { maxFbWidth, maxFbHeight } };
351                 renderPassData.framebufferSize = { maxFbWidth, maxFbHeight };
352             }
353         }
354     }
355 
356     {
357         const RenderPassHashes rpHashes =
358             HashBeginRenderPass(beginRenderPass, renderPassData.renderPassCompatibilityDesc, gpuResourceMgr_);
359         renderPassData.renderPassCompatibilityHash = rpHashes.renderPassCompatibilityHash;
360         renderPassData.renderPassHash = rpHashes.renderPassHash;
361         renderPassData.frameBufferHash = rpHashes.frameBufferHash;
362     }
363 
364     const DeviceVk& deviceVk = (const DeviceVk&)device_;
365     const VkDevice device = ((const DevicePlatformDataVk&)device_.GetPlatformData()).device;
366     const uint64_t frameCount = device_.GetFrameCount();
367 
368     {
369         auto& cache = renderPassCompatibilityCache_;
370         if (const auto iter = cache.hashToElement.find(renderPassData.renderPassCompatibilityHash);
371             iter != cache.hashToElement.cend()) {
372             renderPassData.renderPassCompatibility = iter->second.renderPass;
373         } else { // new
374             renderPassData.renderPassCompatibility = renderPassCreator_.CreateRenderPassCompatibility(
375                 deviceVk, beginRenderPass.renderPassDesc, renderPassData, beginRenderPass.subpasses);
376             cache.hashToElement[renderPassData.renderPassCompatibilityHash] = { 0,
377                 renderPassData.renderPassCompatibility };
378 #if (RENDER_VULKAN_VALIDATION_ENABLED == 1)
379             GpuResourceUtil::DebugObjectNameVk(device_, VK_OBJECT_TYPE_RENDER_PASS,
380                 VulkanHandleCast<uint64_t>(renderPassData.renderPassCompatibility), debugName_ + "_rp_compatibility");
381 #endif
382         }
383     }
384 
385     {
386         auto& cache = framebufferCache_;
387         if (auto iter = cache.hashToElement.find(renderPassData.frameBufferHash); iter != cache.hashToElement.cend()) {
388             iter->second.frameUseIndex = frameCount;
389             renderPassData.framebuffer = iter->second.frameBuffer;
390         } else { // new
391             renderPassData.framebuffer = CreateFramebuffer(
392                 gpuResourceMgr_, beginRenderPass.renderPassDesc, device, renderPassData.renderPassCompatibility);
393             cache.hashToElement[renderPassData.frameBufferHash] = { frameCount, renderPassData.framebuffer };
394 #if (RENDER_VULKAN_VALIDATION_ENABLED == 1)
395             GpuResourceUtil::DebugObjectNameVk(device_, VK_OBJECT_TYPE_FRAMEBUFFER,
396                 VulkanHandleCast<uint64_t>(renderPassData.framebuffer),
397                 debugName_ + "_fbo_" + to_string(renderPassData.framebufferSize.width) + "_" +
398                     to_string(renderPassData.framebufferSize.height));
399 #endif
400         }
401     }
402 
403     {
404         auto& cache = renderPassCache_;
405         if (const auto iter = cache.hashToElement.find(renderPassData.renderPassHash);
406             iter != cache.hashToElement.cend()) {
407             iter->second.frameUseIndex = frameCount;
408             renderPassData.renderPass = iter->second.renderPass;
409         } else { // new
410             renderPassData.renderPass = renderPassCreator_.CreateRenderPass(deviceVk, beginRenderPass, renderPassData);
411             cache.hashToElement[renderPassData.renderPassHash] = { frameCount, renderPassData.renderPass };
412 #if (RENDER_VULKAN_VALIDATION_ENABLED == 1)
413             GpuResourceUtil::DebugObjectNameVk(device_, VK_OBJECT_TYPE_RENDER_PASS,
414                 VulkanHandleCast<uint64_t>(renderPassData.renderPass), debugName_ + "_rp");
415 #endif
416         }
417     }
418 
419     return renderPassData;
420 }
421 
422 #if ((RENDER_VALIDATION_ENABLED == 1) || (RENDER_VULKAN_VALIDATION_ENABLED == 1))
SetValidationDebugName(const string_view debugName)423 void NodeContextPoolManagerVk::SetValidationDebugName(const string_view debugName)
424 {
425     debugName_ = debugName;
426 }
427 #endif
428 RENDER_END_NAMESPACE()
429