Vulkan學習筆記6—渲染呈現

一、渲染循環核心

    while (!glfwWindowShouldClose(window)) {glfwPollEvents();helloTriangleApp.drawFrame(); // 繪制幀}

在 Vulkan 中渲染幀包含一組常見的步驟

  • 等待前一幀完成(vkWaitForFences)

  • 從交換鏈獲取圖像(vkAcquireNextImageKHR)

  • 錄制一個命令緩沖區將場景繪制到圖像上(vkBeginCommandBuffer、vkEndCommandBuffer)

  • 提交已記錄的命令緩沖區(vkQueueSubmit)

  • 呈現交換鏈圖像(vkQueuePresentKHR)

這就是一個drawFrame函數要做的主要工作。

二、同步

GPU執行需顯式同步。

例如下面這些事件:

  • 從交換鏈獲取圖像(vkAcquireNextImageKHR)

  • 執行在獲取的圖像上繪制的命令

  • 將該圖像呈現到屏幕上進行呈現,將其返回到交換鏈(vkQueuePresentKHR)

信號量

用于控制GPU上的同步操作。

VkCommandBuffer A, B = ... // 錄制命令緩沖
VkSemaphore S = ... // 創建一個信號// 當操作 A 完成時,將發出信號量 S 的信號,而操作 B 將不會啟動,直到 S 發出信號
vkQueueSubmit(work: A, signal: S, wait: None)// 在操作 B 開始執行后,信號量 S 將自動重置回未發出信號的狀態,從而允許再次使用它。
vkQueueSubmit(work: B, signal: None, wait: S)

柵欄

它是用于對 CPU(也稱為主機)上的執行進行排序的。如果主機需要知道 GPU 何時完成某件事,我們會使用柵欄。

例如:截屏操作

VkCommandBuffer A = ... // 記錄包含傳輸操作的命令緩沖區
VkFence F = ... // 創建圍欄對象// 將命令緩沖區A提交到隊列,立即開始執行,并在完成時發出圍欄F的信號
vkQueueSubmit(work: A, fence: F)vkWaitForFence(F) // 阻塞當前執行線程,直到命令緩沖區A完成執行save_screenshot_to_disk() // 必須等待傳輸操作完成后才能執行

三、繪制過程

創建同步對象

void HelloTriangle::createSyncObjects() {// 為每一幀預分配同步對象的存儲imageAvailableSemaphores.resize(MAX_CONCURRENT_FRAMES);renderFinishedSemaphores.resize(MAX_CONCURRENT_FRAMES);inFlightFences.resize(MAX_CONCURRENT_FRAMES);// 設置信號量創建信息結構體VkSemaphoreCreateInfo semaphoreInfo{};semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;// 設置圍欄創建信息結構體,初始化為已信號化狀態VkFenceCreateInfo fenceInfo{};fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;  // 初始狀態設為已信號化,允許第一幀立即執行// 為每一個并發幀創建一組同步對象for (size_t i = 0; i < MAX_CONCURRENT_FRAMES; i++) {if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS|| vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS|| vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {throw std::runtime_error("Failed to create synchronization objects for a frame!");}}
}

錄制命令

void HelloTriangle::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {// 1. 開始記錄命令緩沖區VkCommandBufferBeginInfo beginInfo{};beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {throw std::runtime_error("Failed to begin recording command buffer!");}// 2. 設置渲染通道信息VkRenderPassBeginInfo renderPassInfo{};renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;renderPassInfo.renderPass = renderPass;  // 指定使用的渲染通道renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];  // 指定目標幀緩沖renderPassInfo.renderArea.offset = {0, 0};  // 渲染區域起點renderPassInfo.renderArea.extent = swapChainExtent;  // 渲染區域大小// 設置清除顏色 (黑色)VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};renderPassInfo.clearValueCount = 1;renderPassInfo.pClearValues = &clearColor;// 3. 開始渲染通道,使用內聯子通道內容vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);// 4. 綁定圖形管線vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);// 5. 設置視口 (Viewport) - 定義裁剪空間坐標到幀緩沖坐標的映射VkViewport viewport{};viewport.x = 0.0f;viewport.y = 0.0f;viewport.width = static_cast<float>(swapChainExtent.width);viewport.height = static_cast<float>(swapChainExtent.height);viewport.minDepth = 0.0f;viewport.maxDepth = 1.0f;vkCmdSetViewport(commandBuffer, 0, 1, &viewport);// 6. 設置剪刀區域 (Scissor) - 定義實際渲染的區域VkRect2D scissor{};scissor.offset = {0, 0};scissor.extent = swapChainExtent;vkCmdSetScissor(commandBuffer, 0, 1, &scissor);// 7. 執行繪制命令 - 繪制3個頂點,組成一個三角形vkCmdDraw(commandBuffer, 3, 1, 0, 0);// 8. 結束渲染通道vkCmdEndRenderPass(commandBuffer);// 9. 結束命令緩沖區記錄if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {throw std::runtime_error("Failed to record command buffer!");}
}

繪制幀

void HelloTriangle::drawFrame() {// 1. 等待當前幀的圍欄被信號化,確保上一幀的渲染操作已完成vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);// 重置圍欄狀態,為下一幀做準備vkResetFences(device, 1, &inFlightFences[currentFrame]);// 2. 從交換鏈獲取下一可用圖像,使用當前幀的"圖像可用"信號量uint32_t imageIndex;vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);// 3. 重置并記錄當前幀的命令緩沖區vkResetCommandBuffer(commandBuffers[currentFrame],  0);recordCommandBuffer(commandBuffers[currentFrame], imageIndex);// 4. 設置提交信息,定義命令執行的依賴關系VkSubmitInfo submitInfo{};submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;// 等待"圖像可用"信號量,確保在圖像可用后再開始渲染VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};submitInfo.waitSemaphoreCount = 1;submitInfo.pWaitSemaphores = &imageAvailableSemaphores[currentFrame];submitInfo.pWaitDstStageMask = waitStages;// 指定要執行的命令緩沖區submitInfo.commandBufferCount = 1;submitInfo.pCommandBuffers = &commandBuffers[currentFrame];// 當命令執行完成時,信號化"渲染完成"信號量submitInfo.signalSemaphoreCount = 1;submitInfo.pSignalSemaphores = &renderFinishedSemaphores[currentFrame];// 提交命令到圖形隊列,并關聯圍欄以跟蹤完成狀態if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {throw std::runtime_error("Failed to submit draw command buffer!");}// 5. 設置呈現信息,準備將渲染結果呈現到屏幕VkPresentInfoKHR presentInfo{};presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;// 等待"渲染完成"信號量,確保渲染完成后再呈現presentInfo.waitSemaphoreCount = 1;presentInfo.pWaitSemaphores = &renderFinishedSemaphores[currentFrame];// 指定要呈現的交換鏈和圖像索引presentInfo.swapchainCount = 1;presentInfo.pSwapchains = &swapChain;presentInfo.pImageIndices = &imageIndex;// 提交呈現請求到呈現隊列vkQueuePresentKHR(presentQueue, &presentInfo);// 6. 更新當前幀索引,循環使用預分配的同步對象currentFrame = (currentFrame + 1) % MAX_CONCURRENT_FRAMES;
}

完整的初始化過程關系圖

呈現效果:

四、新增成員變量和成員函數

新增/更新的成員變量

// 全局變量
const int MAX_CONCURRENT_FRAMES = 3; // 定義繪制的最大并行幀數// 類成員變量更新
std::vector<VkCommandBuffer> commandBuffers;  // 為每一幀創建一個命令緩沖
std::vector<VkSemaphore> imageAvailableSemaphores; // 為每一幀創建一個圖片信號量
std::vector<VkSemaphore> renderFinishedSemaphores;  // 為每一幀創建渲染器完成信號量
std::vector<VkFence> inFlightFences;  // 為每一幀創建一個柵欄

新增成員函數

    void createCommandBuffers();void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex);void createSyncObjects();void cleanupSwapChain()void drawFrame();

上一節和本節代碼保存為分支 04_render_present

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83631.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83631.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83631.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

React第六十二節 Router中 createStaticRouter 的使用詳解

前言 createStaticRouter 是 React Router 專為 服務端渲染&#xff08;SSR&#xff09; 設計的 API&#xff0c;用于在服務器端處理路由匹配和數據加載。它在構建靜態 HTML 響應時替代了客戶端的 BrowserRouter&#xff0c;確保 SSR 和客戶端 Hydration 的路由狀態一致。 一…

qt 雙緩沖案例對比

雙緩沖 1.雙緩沖原理 單緩沖&#xff1a;在paintEvent中直接繪制到屏幕&#xff0c;繪制過程被用戶看到 雙緩沖&#xff1a;先在redrawBuffer繪制到緩沖區&#xff0c;然后一次性顯示完整結果 代碼結構 單緩沖&#xff1a;所有繪制邏輯在paintEvent中 雙緩沖&#xff1a;繪制…

華為云AI開發平臺ModelArts

華為云ModelArts&#xff1a;重塑AI開發流程的“智能引擎”與“創新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企業擁抱AI的意愿空前高漲&#xff0c;但技術門檻高、流程復雜、資源投入巨大的現實&#xff0c;卻讓許多創新構想止步于實驗室。數據科學家…

ParaGraphX [特殊字符]

https://github.com/stevechampion1/paragraphx 一個基于 JAX 的、為 CPU/GPU 加速而生的超高性能圖算法庫。 ParaGraphX 是一個實驗性的 Python 庫&#xff0c;旨在利用 JAX 的即時編譯 (JIT) 和大規模并行計算能力&#xff0c;為經典的圖算法提供驚人的性能提升。我們的目標…

如何用4 種可靠的方法更換 iPhone(2025 年指南)

Apple 每年都會發布新版本的 iPhone。升級到新 iPhone 是一種令人興奮的體驗&#xff0c;但轉移所有寶貴數據的想法有時會讓人感到畏懼。幸運的是&#xff0c;我們準備了 4 種有效的更換 iPhone 的方法&#xff0c;讓你可以毫不費力地更換到你的新 iPhone。 此外&#xff0c;您…

GitLab 拉取變慢的原因及排查方法

前言&#xff1a;在軟件開發的快節奏世界里&#xff0c;高效協作與快速交付是制勝關鍵。然而&#xff0c;當開發團隊興高采烈地投入工作&#xff0c;卻發現從GitLab拉取代碼的速度慢如蝸牛&#xff0c;那種沮喪感簡直能瞬間澆滅熱情。在分布式開發環境中&#xff0c;這種情況時…

落水人員目標檢測數據集(貓臉碼客第253期)

落水人員目標檢測&#xff1a;科技守護生命之舟 一、背景與意義 隨著人類海洋活動和水上活動的日益頻繁&#xff0c;海上與水域安全事故頻發。每年都會開展大量的海上救援行動&#xff0c;以搜救數以萬計的落難人員。在水上活動區域&#xff0c;如水庫、河道等&#xff0c;溺…

JAVA_強制類型轉換:

類型范圍大的變量&#xff0c;不可以直接賦值給類型變量小的變量 需要進行強制類型轉換&#xff1a; 想要完成類型范圍大的變量傳給類型范圍小的變量需要先創建一個新的變量&#xff08;類型與方法的形參類型要相同&#xff09;。將類型范圍大的變量前面加上&#xff08;轉換類…

打卡第44天:無人機數據集分類

重復以下內容 作業&#xff1a; kaggle找到一個圖像數據集&#xff0c;用cnn網絡進行訓練并且用grad-cam做可視化 進階&#xff1a; 并拆分成多個文件 import os import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader,…

個人網站大更新,還是有個總站比較好

個人網站大更新&#xff0c;還是有個總站比較好 放棄了所有框架&#xff0c;用純htmlcssjs擼了個網站&#xff0c;這回可以想改啥改啥了。 選擇了黑紫作為主色調&#xff0c;暫時看著還算可以。 為什么不用那些框架了 幾個原因&#xff1a; 嘗試用vuepress、vitepress、not…

高精度算法詳解:從原理到加減乘除的完整實現

文章目錄 一、為什么需要高精度算法二、高精度算法的數據結構設計2.1 基礎工具函數2.2 高精度加法實現2.3 高精度減法實現2.4 高精度乘法實現2.5 高精度除法實現 三、完整測試程序四、總結 一、為什么需要高精度算法 在編程中&#xff0c;處理極大數值是常見需求&#xff0c;例…

排序--計數排序

一,引言 計數排序是一種針對整數數據的高效排序算法。其主要流程可分為三個步驟&#xff1a;首先計算整數數據的數值范圍&#xff1b;接著按大小順序統計各數值的出現次數&#xff1b;最后根據統計結果輸出排序后的數據序列。 二,求最值 遍歷現有數據&#xff0c;獲取最大值…

Kubernetes安全機制深度解析(四):動態準入控制和Webhook

#作者&#xff1a;程宏斌 文章目錄 動態準入控制什么是準入 Webhook&#xff1f; 嘗試準入Webhook先決條件編寫一個準入 Webhook 服務器部署準入 Webhook 服務即時配置準入 Webhook對 API 服務器進行身份認證 Webhook 請求與響應Webhook 配置匹配請求-規則匹配請求&#xff1a…

WDK 10.0.19041.685,可在32位win7 sp1系統下搭配vs2019使用,可以編譯出xp驅動。

(14)[驅動開發]配置環境 VS2019 WDK10 寫 xp驅動 (14)[驅動開發]配置環境 VS2019 WDK10 寫 xp驅動_microsoft visual 2019 wdk-CSDN博客文章瀏覽閱讀3k次&#xff0c;點贊8次&#xff0c;收藏17次。本文介紹了如何在VS2019環境下安裝和配置Windows Driver Kit(WDK)&#xff0…

論壇系統自動化測試

1、項目背景與測試目標 系統定位 論壇系統作為典型的高并發Web應用&#xff0c;需支持用戶注冊、登錄、發帖、評論、私信及個人中心管理等核心功能&#xff0c;是用戶公開交流與信息共享的核心平臺。其穩定性與響應效率直接影響用戶體驗及平臺活躍度。 測試必要性 功能可靠性&…

ChipWhisperer教程(一)

一、ChipWhisperer介紹 ChipWhisperer 是一個完整的開源工具鏈&#xff0c;用于學習嵌入式設備上的側信道攻擊并驗證這些設備的側信道抗性。ChipWhisperer主要用于功耗分析&#xff0c;利用設備功耗泄露的信息進行攻擊&#xff0c;也可用于故障攻擊&#xff08;電壓和時鐘毛刺…

【持續更新】計算機網絡試題

問題1 請簡要說明TCP/IP協議棧的四層結構&#xff0c;并分別舉出每一層出現的典型協議或應用。 答案 應用層&#xff1a;ping,telnet,dns 傳輸層&#xff1a;tcp,udp 網絡層&#xff1a;ip,icmp 數據鏈路層&#xff1a;arp,rarp 問題2 下列協議或應用分別屬于TCP/IP協議…

短劇系統開發:打造高效、創新的短視頻娛樂平臺 - 從0到1的完整解決方案

一、短劇市場迎來爆發式增長 - 不容錯過的萬億級藍海 隨著5G技術的普及和移動互聯網的深度滲透&#xff0c;短劇市場正在經歷前所未有的爆發式增長。根據權威機構艾瑞咨詢最新發布的《2023年中國網絡短劇行業發展報告》顯示&#xff1a; 市場規模&#xff1a;2023年中國短劇市…

ChipWhisperer教程(三)

——CW305目標板的波形采集 一、目標板介紹 CW305 是一款獨立的 FPGA 目標板&#xff0c;搭載的FPGA芯片為Xilinx Artix-7系列。 它具有與 FPGA 通信的 USB 接口、為 FPGA 提供時鐘的外部 PLL、編程 VCC-INT 電源以及用于故障注入環境的二極管保護。 CW305 電路板有多種配置&…

django中如何解析content-type=application/json的請求

django中如何解析content-typeapplication/json的請求 本文由「大千AI助手」原創發布&#xff0c;專注用真話講AI&#xff0c;回歸技術本質。拒絕神話或妖魔化。搜索「大千AI助手」關注我&#xff0c;一起撕掉過度包裝&#xff0c;學習真實的AI技術&#xff01; 往期文章回顧: …