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