?前言
? ? ?上一章深入剖析了GPU資源內存及其管理,vsg中為了提高設備內存的利用率,同時減少內存(GPU)碎片,采用GPU資源內存池機制(vsg::MemoryBufferPools)管理邏輯緩存(VkBuffer)與物理內存(VkDeviceMemory)。本章將深入vsg中vulkan資源的編譯(包含視景器編譯),著重于當vsg::TransferTask不為空時,vulkan資源的創建與收集過程。
目錄
- 1 視景器編譯
- 2 VertexIndexDraw的編譯
1 視景器編譯
? ? ? ?如下為Viewer.cpp中387-393行代碼,對視景器中的所有vsg::RecordAndSubmitTask對象執行編譯任務,其中RecordAndSubmitTask 用于錄制其 CommandGraph 列表到 CommandBuffer,并最終將這些命令緩沖區提交到對應的 Vulkan 隊列,vsg::Viewer依賴于一個vsg::RecordAndSubmitTask數組,其創建的詳細過程在vsg::Viewer::assignRecordAndSubmitTaskAndPresentation函數中體現。
for (auto& task : recordAndSubmitTasks){auto& deviceResource = deviceResourceMap[task->device];auto& resourceRequirements = deviceResource.collectResources.requirements;compileManager->compileTask(task, resourceRequirements);task->transferTask->assign(resourceRequirements.dynamicData);}
? ? ? ?如下為數據編譯過程中的類圖關系:
? ? ? ?vsg::RecordAndSubmitTask與vsg::DatabasePager依賴于vsg::CompileManager進行數據編譯,分別對應視景器的編譯、節點動態加載后的編譯。vsg::CompileManager是一個輔助類,用于編譯與vsg::CompileManager相關聯的窗口(vsg::Windows)/幀緩沖區(vsg::Framebuffer)的子圖。vsg::CompileManager依賴于vsg::CompileTraversal對子圖進行遍歷,其繼承自vsg::Visitor。CompileTraversal 會遍歷場景圖(scene graph),并調用所有 StateCommand/Command::compile(..) 方法,以創建 Vulkan 對象、分配 GPU 內存,當vsg::TransferTask對象為空時,將數據傳輸到 GPU。以BufferInfo.cpp中vsg::createBufferAndTransferData函數為例(280-294行),當vsg::TransferTask對象非空時,將待傳輸的數據信息記錄到vsg::TransferTask對象中:
if (transferTask){vsg::debug("vsg::createBufferAndTransferData(..)");for (auto& bufferInfo : bufferInfoList){vsg::debug(" ", bufferInfo, ", ", bufferInfo->data, ", ", bufferInfo->buffer, ", ", bufferInfo->offset);bufferInfo->data->dirty();bufferInfo->parent = deviceBufferInfo;}transferTask->assign(bufferInfoList);return true;}
? ? ? ?vsg::CompileTraversal中存在多個vsg::Context,而vsg::Compileable節點的編譯依賴vsg::Context,即同一個vsg::Compileable節點可以依賴多個vsg::Context對象編譯多次,以void CompileTraversal::apply(Commands& commands)函數為例:
void CompileTraversal::apply(Commands& commands)
{CPU_INSTRUMENTATION_L3_NC(instrumentation, "CompileTraversal Commands", COLOR_COMPILE);for (auto& context : contexts){commands.compile(*context);}
}
? ?vsg::Context的創建賴于vsg::View,如下為函數void CompileTraversal::add(const Viewer& viewer, const ResourceRequirements& resourceRequirements)215-225行:
void apply(View& view) override
{if (!objectStack.empty()){auto obj = objectStack.top();if (auto window = obj.cast<Window>())ct->add(*window, transferTask, ref_ptr<View>(&view), resourceRequirements);else if (auto framebuffer = obj.cast<Framebuffer>())ct->add(*framebuffer, transferTask, ref_ptr<View>(&view), resourceRequirements);}
}
? ? ? ?上方代碼ct->add(...)的調用具體實現如下:
void CompileTraversal::add(Window& window, ref_ptr<TransferTask> transferTask, ref_ptr<ViewportState> viewport, const ResourceRequirements& resourceRequirements)
{auto device = window.getOrCreateDevice();auto renderPass = window.getOrCreateRenderPass();auto queueFamily = device->getPhysicalDevice()->getQueueFamily(queueFlags);auto context = Context::create(device, resourceRequirements);context->instrumentation = instrumentation;context->renderPass = renderPass;context->commandPool = CommandPool::create(device, queueFamily, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);context->graphicsQueue = device->getQueue(queueFamily, queueFamilyIndex);context->transferTask = transferTask;if (viewport)context->defaultPipelineStates.emplace_back(viewport);elsecontext->defaultPipelineStates.emplace_back(vsg::ViewportState::create(window.extent2D()));if (renderPass->maxSamples != VK_SAMPLE_COUNT_1_BIT) context->overridePipelineStates.emplace_back(MultisampleState::create(renderPass->maxSamples));contexts.push_back(context);
}
? ? ? ? 總結來說,在CompileTraversal創建過程中,通過遍歷場景樹,遍歷所有vsg::View,逐一添加vsg::Context對象,接著利用CompileTraversal遍歷場景樹,遍歷所有vsg::Context對象,逐一編譯vsg::Compilable節點。
2 VertexIndexDraw的編譯
? ? ? 回到第4章(vulkanscenegraph顯示傾斜模型(4)-數據讀取-CSDN博客)懸疑列表之一的osg::Geometry轉vsg::Command的具體細節,其最終將osg::Geometry轉化為了vsg:VertexIndexDraw對象,本節將以vsg::VertexIndexDraw為例,深入節點(繼承自vsg::Compilable)的編譯過程。
void VertexIndexDraw::compile(Context& context)
{if (arrays.empty() || !indices){// VertexIndexDraw does not contain required arrays and indicesreturn;}auto deviceID = context.deviceID;bool requiresCreateAndCopy = false;if (indices->requiresCopy(deviceID))requiresCreateAndCopy = true;else{for (auto& array : arrays){if (array->requiresCopy(deviceID)){requiresCreateAndCopy = true;break;}}}if (requiresCreateAndCopy){BufferInfoList combinedBufferInfos(arrays);combinedBufferInfos.push_back(indices);createBufferAndTransferData(context, combinedBufferInfos, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_SHARING_MODE_EXCLUSIVE);// info("VertexIndexDraw::compile() create and copy ", this);}else{// info("VertexIndexDraw::compile() no need to create and copy ", this);}assignVulkanArrayData(deviceID, arrays, _vulkanData[deviceID]);
}
? ? 其中arrays變量記錄頂點以及頂點屬性,indices變量記錄三角網索引。主要過程為創建 Vulkan 對象、分配 GPU 內存(createBufferAndTransferData),而assignVulkanArrayData函數僅將arrays中的Vulkan對象和偏移以數組連續的方式存儲在_vulkanData[deviceId]對象中,方便后續vsg:VertexIndexDraw::record函數的調用。創建 Vulkan 對象、分配 GPU 內存過程中的依賴關系如下:
文末:本章在上一章的基礎上,深入分析VSG中Vulkan資源的編譯過程,并以vsg::VertexIndexDraw為例進行詳解。在VSG中,資源編譯主要包括創建Vulkan對象和分配GPU內存兩個核心步驟。當vsg::TransferTask非空時,程序會將待傳輸的數據信息記錄至該對象,以便后續處理。下一章將重點剖析VSG中的數據傳輸(Transfer)機制。