diff --git a/src/render/render.cpp b/src/render/render.cpp index dc4c0f9..3303170 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -39,11 +39,7 @@ void PlumageRender::renderMain::framebufferResizeCallback(GLFWwindow* window, in // todo:重写成glfw的 void PlumageRender::renderMain::windowResized() { - buildCommandBuffers(); - vkDeviceWaitIdle(device); - updateUniformBuffers(); - //update UI - updateUIOverlay(); + } void PlumageRender::renderMain::prepare() @@ -52,18 +48,18 @@ void PlumageRender::renderMain::prepare() setupCamera(); - loadAssets(); - generateBRDFLUT(); - generateCubemaps(); + renderInput.loadAssets(); + pbrMaterial.generateBRDFLUT(); + pbrMaterial.generateCubemap(); //prepareUniformBuffers(); //setupDescriptors(); - preparePipelines(); + //preparePipelines(); if (!PlumageRender::Setter::settings.headless) { - gui = new UI(vulkanDevice, renderPass, queue, pipelineCache,PlumageRender::Setter::settings.sampleCount); - updateUIOverlay(); + auto gui = renderGUI.gui; + renderGUI.updateUIOverlay(); } - buildCommandBuffers(); + ; prepared = true; } @@ -86,12 +82,18 @@ void PlumageRender::renderMain::submitWork(VkCommandBuffer cmdBuffer, VkQueue qu submitInfo.pCommandBuffers = &cmdBuffer; VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo(); VkFence fence; - VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence)); + VK_CHECK_RESULT(vkCreateFence(VulkanBackend::VulkanFoundation::device, &fenceInfo, nullptr, &fence)); VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); - VK_CHECK_RESULT(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX)); - vkDestroyFence(device, fence, nullptr); + VK_CHECK_RESULT(vkWaitForFences(VulkanBackend::VulkanFoundation::device, 1, &fence, VK_TRUE, UINT64_MAX)); + vkDestroyFence(VulkanBackend::VulkanFoundation::device, fence, nullptr); } +void PlumageRender::renderMain::submitToPresentQueue() +{ + + +} + void PlumageRender::renderMain::render() { if (!prepared) { @@ -100,7 +102,7 @@ void PlumageRender::renderMain::render() if (!PlumageRender::Setter::settings.headless) { - updateUIOverlay(); + renderGUI.updateUIOverlay(); } //加入写到文件的函数 @@ -108,92 +110,113 @@ void PlumageRender::renderMain::render() //outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath); - outputImageSequence(); - VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX)); + uint32_t imageIndex; - imageSequenceToVideo(); - VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex])); + VK_CHECK_RESULT(vkWaitForFences(VulkanBackend::VulkanFoundation::device, 1, &vkFoundation.waitFences[frameIndex], VK_TRUE, UINT64_MAX)); + VkResult result = vkAcquireNextImageKHR(VulkanBackend::VulkanFoundation::device, VulkanBackend::VulkanFoundation::swapChain, UINT64_MAX, vkFoundation.renderCompleteSemaphores[frameIndex], VK_NULL_HANDLE, &imageIndex); - VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], ¤tBuffer); - if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) { - windowResize(); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) + { + framebufferResized = false; + vkFoundation.recreateSwapChain(); + return; } - else { - VK_CHECK_RESULT(acquire); - - + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) + { + throw std::runtime_error("failed to acquire swap chain image in drawFrame"); } - + + VK_CHECK_RESULT(vkResetFences(VulkanBackend::VulkanFoundation::device, 1, &vkFoundation.waitFences[frameIndex])); + renderOutput.outputImageSequence(); + renderOutput.imageSequenceToVideo(); // Update UBOs - updateUniformBuffers(); - UniformBufferSet[currentUB] = uniformBuffers[currentBuffer]; - memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene)); - memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData)); - memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox)); + vkFoundation.updateUniformBuffers(); + VulkanBackend::VulkanFoundation::UniformBufferSet currentUB = vkFoundation.uniformBuffers[currentBuffer]; + memcpy(currentUB.scene.mapped, &vkFoundation.shaderDataScene, sizeof(vkFoundation.shaderDataScene)); + memcpy(currentUB.params.mapped, &PBR::Material::shaderData, sizeof(PBR::Material::shaderData)); + memcpy(currentUB.skybox.mapped, &vkFoundation.shaderDataSkybox, sizeof(vkFoundation.shaderDataSkybox)); const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pWaitDstStageMask = &waitDstStageMask; - submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; + submitInfo.pWaitSemaphores = &vkFoundation.presentCompleteSemaphores[frameIndex]; submitInfo.waitSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; + submitInfo.pSignalSemaphores = &vkFoundation.renderCompleteSemaphores[frameIndex]; submitInfo.signalSemaphoreCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[currentBuffer]; + submitInfo.pCommandBuffers = &vkFoundation.commandbuffers[currentBuffer]; submitInfo.commandBufferCount = 1; - VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex])); + VK_CHECK_RESULT(vkQueueSubmit(vkFoundation.graphicQueue, 1, &submitInfo, vkFoundation.waitFences[frameIndex])); //显示队列 - - VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]); - if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) { - if (present == VK_ERROR_OUT_OF_DATE_KHR) { - windowResize(); - return; - } - else { - VK_CHECK_RESULT(present); - } + + VkSemaphore signalSemaphores[] = { vkFoundation.renderCompleteSemaphores[frameIndex] }; + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = { vkFoundation.swapChain }; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + result = vkQueuePresentKHR(vkFoundation.presentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) + { + framebufferResized = false; + vkFoundation.recreateSwapChain(); + } + else if (result != VK_SUCCESS) + { + throw std::runtime_error("failed to present swap chain image in drawFrame"); } frameIndex += 1; - frameIndex %= renderAhead; + frameIndex %= PlumageRender::Setter::settings.MaxFrameInFlight; - if (!paused) { + if (!PlumageRender::Setter::settings.pause) { if (PlumageRender::Setter::settings.rotateModel) { modelrot.y += frameTimer * 35.0f; if (modelrot.y > 360.0f) { modelrot.y -= 360.0f; } } - if ((animate) && (models.scene.animations.size() > 0)) { + if ((PlumageRender::Setter::settings.animate) && (models.scene.animations.size() > 0)) { animationTimer += frameTimer; if (animationTimer > models.scene.animations[animationIndex].end) { animationTimer -= models.scene.animations[animationIndex].end; } models.scene.updateAnimation(animationIndex, animationTimer); } - updateShaderData(); + vkFoundation.updateShaderData(); if (PlumageRender::Setter::settings.rotateModel) { - updateUniformBuffers(); + vkFoundation.updateUniformBuffers(); } } if (camera.updated) { - updateUniformBuffers(); + vkFoundation.updateUniformBuffers(); } } -void PlumageRender::renderMain::fileDropped(std::string filename) + void PlumageRender::renderMain::renderLoop(GLFWwindow* window) + { + while (!glfwWindowShouldClose(window)) { - vkDeviceWaitIdle(device); - loadScene(filename); - setupDescriptors(); - buildCommandBuffers(); - + glfwPollEvents(); + render(); } + vkDeviceWaitIdle(VulkanBackend::VulkanFoundation::device); + } + + + @@ -233,15 +256,13 @@ PlumageRender* plumageRender; int main() { PlumageRender::renderMain plumageRender; - VulkanBackend::VulkanFoundation vkFoundation; - + VulkanBackend::VulkanFoundation vkFoundation; vkFoundation.initVulkan(); if (!PlumageRender::Setter::settings.headless) { plumageRender.initWindow(PlumageRender::Setter::settings.width,PlumageRender::Setter::settings.height); } - return 0; } diff --git a/src/render/render.h b/src/render/render.h index a23a522..e5b23cf 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -72,7 +72,13 @@ namespace PlumageRender uint64_t savedFrameCounter = PlumageRender::Setter::settings.startFrameCount; - bool framebufferResized = false; + + + uint32_t lastFPS = 0; + static const float frameTimer = 1.0f; + + static int32_t animationIndex; + float animationTimer = 0.0f; renderMain(); ~renderMain() @@ -90,9 +96,26 @@ namespace PlumageRender void prepare(); void setupCamera(); void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue); + void submitToPresentQueue(); + virtual void render(); - virtual void fileDropped(std::string filename); + void renderLoop(GLFWwindow* window); private: + + PlumageRender::RenderInput renderInput; + + PlumageRender::RenderOutput renderOutput; + + PBR::Material pbrMaterial; + + PlumageRender::PlumageGUI renderGUI; + + VulkanBackend::VulkanFoundation vkFoundation; + + bool framebufferResized = false; + + bool prepared = false; + }; diff --git a/src/render/renderIO.h b/src/render/renderIO.h index a5a1882..817ebd8 100644 --- a/src/render/renderIO.h +++ b/src/render/renderIO.h @@ -24,14 +24,13 @@ namespace PlumageRender void loadEnvironment(std::string fileName); - static void loadAssets(); - - static int32_t animationIndex; + void loadAssets(); + private: - float animationTimer = 0.0f; + }; diff --git a/src/render/renderSetter.h b/src/render/renderSetter.h index 02f6d6e..23b5e80 100644 --- a/src/render/renderSetter.h +++ b/src/render/renderSetter.h @@ -33,12 +33,13 @@ namespace PlumageRender bool outputPNGimage = false; // 输出图片序列格式为PNG(四通道) bool enableSaveToImageSequeue = false; // 图片序列开关(暂时弃用) bool animate = true; // 动画开关 + bool pause = false; // 暂停模型旋转(暂时弃用) uint32_t MaxFrameInFlight = 2; // 最大并行渲染帧数(通常为2,此时CPU和GPU并行处理) uint32_t endFrameIndex = 75; // 图片序列结束帧 bool enableIMGUI = false; // gui使用imgui uint32_t startFrameCount = 1; // 图片序列开始帧 uint32_t videoFrameRate = 25; // 视频帧率 - uint32_t selectedPhysicalDeviceIndex = 0; + uint32_t selectedPhysicalDeviceIndex = 0; // 选中的显卡,数组形式从0开始编号 VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; // 多重采样倍率 }; diff --git a/src/render/renderUI.cpp b/src/render/renderUI.cpp index e5d569e..d24fa52 100644 --- a/src/render/renderUI.cpp +++ b/src/render/renderUI.cpp @@ -8,7 +8,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() ImVec2 lastDisplaySize = io.DisplaySize; io.DisplaySize = ImVec2((float)PlumageRender::Setter::settings.width, (float)PlumageRender::Setter::settings.height); - io.DeltaTime = frameTimer; + io.DeltaTime = PlumageRender::renderMain::frameTimer; io.MousePos = ImVec2(mousePos.x, mousePos.y); io.MouseDown[0] = mouseButtons.left; @@ -57,12 +57,15 @@ void PlumageRender::PlumageGUI::updateUIOverlay() vkDeviceWaitIdle(VulkanBackend::VulkanFoundation::device); std::wstring_convert> converter; std::string stringFilename = converter.to_bytes(filename); - PlumageRender::RenderInput::loadScene(stringFilename); - PlumageRender::renderMain::vkFoundation.createDescriptorSets(); + + renderInput.loadScene(stringFilename); + + vkFoundation.createDescriptorSets(); updateCBs = true; - PlumageRender::renderMain::renderOutput.signal.imageSequenceOutputComplete = false; - PlumageRender::renderMain::renderOutput.signal.imageSequenceToVideoComplete = false; - PlumageRender::renderMain::renderOutput.savedFrameCounter = 1; + + renderOutput.signal.imageSequenceOutputComplete = false; + renderOutput.signal.imageSequenceToVideoComplete = false; + renderOutput.savedFrameCounter = 1; } } gui->endMenu(); @@ -71,24 +74,24 @@ void PlumageRender::PlumageGUI::updateUIOverlay() { if (gui->beginMenu(chineseUI.menuEnvironmentConfig)) { - auto& selectedEnvironment = PlumageRender::renderMain::renderInput.selectedEnvironment; - auto& environments = PlumageRender::renderMain::renderInput.environments; + auto& selectedEnvironment = renderInput.selectedEnvironment; + auto& environments = renderInput.environments; if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) { - vkDeviceWaitIdle(PlumageRender::renderMain::vkFoundation.device); - PlumageRender::renderMain::renderInput.loadEnvironment(environments[selectedEnvironment]); - PlumageRender::renderMain::vkFoundation.createDescriptorSets(); + vkDeviceWaitIdle(vkFoundation.device); + renderInput.loadEnvironment(environments[selectedEnvironment]); + vkFoundation.createDescriptorSets(); updateCBs = true; } if (gui->checkbox(chineseUI.environmentBackGround, &PlumageRender::Setter::settings.displayBackground)) { updateShaderParams = true; } - if (gui->slider("Exposure", &PlumageRender::renderMain::vkFoundation.shaderData.exposure, 0.1f, 10.0f)) { + if (gui->slider("Exposure", &PBR::Material::shaderData.exposure, 0.1f, 10.0f)) { updateShaderParams = true; } - if (gui->slider("Gamma", &PlumageRender::renderMain::vkFoundation.shaderData.gamma, 0.1f, 4.0f)) { + if (gui->slider("Gamma", &PBR::Material::shaderData.gamma, 0.1f, 4.0f)) { updateShaderParams = true; } - if (gui->slider("IBL", &PlumageRender::renderMain::vkFoundation.shaderData.scaleIBLAmbient, 0.0f, 1.0f)) { + if (gui->slider("IBL", &PBR::Material::shaderData.scaleIBLAmbient, 0.0f, 1.0f)) { updateShaderParams = true; } gui->endMenu(); @@ -103,7 +106,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() "none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness" }; if (gui->combo(chineseUI.debugInput, &debugView.debugViewInputs, debugNamesInputs)) { - PlumageRender::renderMain::vkFoundation.shaderData.debugViewInputs = static_cast(debugView.debugViewInputs); + PBR::Material::shaderData.debugViewInputs = static_cast(debugView.debugViewInputs); updateShaderParams = true; } gui->endMenu(); @@ -114,7 +117,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() "none", "Diff (l,n)", "F (l,h)", "G (l,v,h)", "D (h)", "Specular" }; if (gui->combo(chineseUI.debugPBREquation, &debugView.debugViewEquation, debugNamesEquation)) { - PlumageRender::renderMain::vkFoundation.shaderData.debugViewEquation = static_cast(debugView.debugViewEquation); + PBR::Material::shaderData.debugViewEquation = static_cast(debugView.debugViewEquation); updateShaderParams = true; } gui->endMenu(); @@ -122,7 +125,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() if (gui->beginMenu(chineseUI.menuDebugFrameRate)) { - gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS)); + gui->text("%.1d fps (%.2f ms)", renderMain.lastFPS, (1000.0f / renderMain.lastFPS)); gui->endMenu(); } gui->endMenu(); @@ -142,7 +145,7 @@ void PlumageRender::PlumageGUI::updateUIOverlay() for (auto animation : PlumageRender::renderMain::models.scene.animations) { animationNames.push_back(animation.name); } - gui->combo(chineseUI.animationSeq, &PlumageRender::RenderInput::animationIndex, animationNames); + gui->combo(chineseUI.animationSeq, &renderMain.animationIndex, animationNames); gui->endMenu(); } } diff --git a/src/render/renderUI.h b/src/render/renderUI.h index a4baa34..a6f0b4d 100644 --- a/src/render/renderUI.h +++ b/src/render/renderUI.h @@ -9,13 +9,13 @@ #include "render.h" namespace PlumageRender { - class PlumageGUI + class PlumageGUI { public: PlumageGUI(); ~PlumageGUI(); - UI* gui; + UI* gui = new UI(VulkanBackend::VulkanFoundation::vulkanDevice, VulkanBackend::VulkanFoundation::renderPass, VulkanBackend::VulkanFoundation::graphicQueue, VulkanBackend::VulkanFoundation::pipelineCache, PlumageRender::Setter::settings.sampleCount); void updateUIOverlay(); @@ -29,10 +29,12 @@ namespace PlumageRender private: - PlumageRender::Setter setter; - uint32_t lastFPS = 0; - float frameTimer = 1.0f; + + PlumageRender::RenderInput renderInput; + VulkanBackend::VulkanFoundation vkFoundation; + PlumageRender::RenderOutput renderOutput; + PlumageRender::renderMain renderMain; struct GamePadState { glm::vec2 axisLeft = glm::vec2(0.0f); diff --git a/src/render/vulkanFoundation.cpp b/src/render/vulkanFoundation.cpp index 74674b0..93ca8cf 100644 --- a/src/render/vulkanFoundation.cpp +++ b/src/render/vulkanFoundation.cpp @@ -1679,6 +1679,21 @@ void VulkanBackend::VulkanFoundation::allocateCommandBuffers() VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandbuffers.data())); } +void VulkanBackend::VulkanFoundation::cleanupSwapChain() +{ + for (auto framebuffer : framebuffers) + { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + + for (auto imageView : swapChainImageViews) + { + vkDestroyImageView(device, imageView, nullptr); + } + + vkDestroySwapchainKHR(device, swapChain, nullptr); +} + void VulkanBackend::VulkanFoundation::createCommandBuffer() { @@ -1883,6 +1898,29 @@ void VulkanBackend::VulkanFoundation::updateShaderData() 0.0f); } +void VulkanBackend::VulkanFoundation::recreateSwapChain() +{ + int width = 0, height = 0; + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) + { + if (glfwWindowShouldClose(window)) + { + return; + } + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(device); + + cleanupSwapChain(); + + createSwapChain(); + createImageView(); + createFramebuffer(); +} + diff --git a/src/render/vulkanFoundation.h b/src/render/vulkanFoundation.h index baade36..54cc7b2 100644 --- a/src/render/vulkanFoundation.h +++ b/src/render/vulkanFoundation.h @@ -35,8 +35,7 @@ namespace VulkanBackend vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr); vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr); - models.scene.destroy(device); - models.skybox.destroy(device); + for (auto buffer : uniformBuffers) { buffer.params.destroy(); @@ -81,6 +80,32 @@ namespace VulkanBackend static std::vector swapChainImages; static VkFormat swapChainImageFormat; + static VkSwapchainKHR swapChain; + + static VkRenderPass renderPass; + + std::vector waitFences; + std::vector renderCompleteSemaphores; + std::vector presentCompleteSemaphores; + + struct UniformBufferSet { + Buffer scene; + Buffer skybox; + Buffer params; + }; + + struct UBOMatrices { + glm::mat4 projection; + glm::mat4 model; + glm::mat4 view; + glm::vec3 camPos; + } shaderDataScene, shaderDataSkybox; + + std::vector uniformBuffers; + + std::vector commandbuffers; + + VkQueue presentQueue; void initVulkan(); @@ -88,9 +113,17 @@ namespace VulkanBackend void createDescriptorSets(); void createCommandBuffer(); + + void updateUniformBuffers(); + + void updateShaderData(); + + void recreateSwapChain(); private: + + VkInstance instance; VkDebugUtilsMessengerEXT debugMessenger; @@ -125,19 +158,14 @@ namespace VulkanBackend VkPhysicalDeviceFeatures deviceFeatures; - VkQueue presentQueue; - - - VkSwapchainKHR swapChain; VkExtent2D swapChainExtent; std::vector swapChainImageViews; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; - VkRenderPass renderPass; + VkCommandPool commandPool; @@ -148,7 +176,7 @@ namespace VulkanBackend std::vector framebuffers; - std::vector commandbuffers; + struct FrameBufferAttachment { @@ -201,30 +229,14 @@ namespace VulkanBackend VkPipelineLayout pipelineLayout; - - struct UniformBufferSet { - Buffer scene; - Buffer skybox; - Buffer params; - }; - - struct UBOMatrices { - glm::mat4 projection; - glm::mat4 model; - glm::mat4 view; - glm::vec3 camPos; - } shaderDataScene, shaderDataSkybox; - struct LightSource { glm::vec3 color = glm::vec3(1.0f); glm::vec3 rotation = glm::vec3(75.0f, 40.0f, 0.0f); } lightSource; - std::vector uniformBuffers; + - std::vector waitFences; - std::vector renderCompleteSemaphores; - std::vector presentCompleteSemaphores; + // 句柄创建,检查校验层支持和获取需要的扩展 void createInstance(); @@ -259,7 +271,8 @@ namespace VulkanBackend VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); - + void cleanupSwapChain(); + // 创建交换链中的imageView,用于访问交换链中图像 void createImageView(); @@ -289,7 +302,7 @@ namespace VulkanBackend // 创建统一缓冲区 void createUniformBuffer(); - void updateUniformBuffers(); + // 创建描述符池 void createDescriptorPool(); @@ -310,7 +323,7 @@ namespace VulkanBackend // 创建栅栏和信号量,用于多帧并行的同步 void createFenceAndSemaphore(); - void updateShaderData(); + };