圖形渲染中關於CPU和GPU的一些問題?

1,cpu和gpu同步問題:

網上能搜到的資料都很亂,說法不一,又不完整。

一個說法是有一個不知道存在哪邊的內存(知道的請告訴我)叫做command buffer。cpu發送的繪製指令會先存在這塊內存中,然後這又有兩種說法:一說是當

command buffer被塞滿,cpu才發送指令給gpu;另一說是command buffer被塞滿,cpu 就會被阻塞等待了,直到gpu執行了當中的一兩條指令,空出位置,cpu

才可以繼續跑繼續往裡面塞指令。

然後另一個說法是以上都不對。說是cpu執行到Draw函數時,就發送繪製指令,當cpu超過gpu 3幀,gpu還沒跑完當前指令,cpu就會阻塞等待(是在glutSwapBuffers()/present()那裡阻塞嗎?)。還有個說法是說在swapBuffers()/present() 函數里才發送繪製指令。再有個說法是程序員要自己維護這個等待同步。(以上均討論雙緩衝的情況,單緩衝的glflush,glfinish不考慮)

請給出詳細的正確的答案~

2,以上等待都是假設Gpu跑得慢,CPU 必須等 GPU。所以,真正準確的幀率應該怎麼計算?一般的幀率計算都只是基於CPU每幀的時間間隔,沒把GPU執行的時間算上去(因為CPU發送指令了不代表GPU就馬上繪製完成了)(另外以上均排除垂直同步的情況,即假設默認關閉垂直同步)

另外關於OpenGL,測量執行命令的時間,glBeginQuery()/glEndQuery()和glQueryCounter(),都只是Cpu端的執行時間嗎?還是GPU端的?比如

glBeginQuery();

DrawWorld();

glEndQuery();

上述是得出的僅僅是Cpu端DrawWorld()的數據發送和繪製命令的發送的時間,還是會加上GPU真正把World Draw出來的時間?glQueryCounter()同問:

glQueryCounter();

DrawWorld();

glQueryCounter();

上述得出什麼時間?

3,glutSwapBuffers()/present() 交換的是什麼的雙緩衝?一個說法是 CPU 到 GPU 是具體經過了幾級。用戶代碼到驅動是一級,驅動到硬體又是一級。我的想法是cpu把數據和指令發送給gpu,gpu同時在取,存取同時進行,所以需要雙緩衝,這是一級(可能就是glutSwapBuffers()/present()做的,但我不肯定,請大神確定下?);而gpu把計算好的像素數據往屏幕幀緩存寫,同時幀緩存又在讀,這個雙緩衝是另一級,是這樣嗎?

具體原理究竟是怎麼樣的?請給出詳細的正確的答案~


1. cpu和gpu同步問題

1)CPU跟GPU的交流當然得提到Driver,Driver分UMD(User Mode Driver)和KMD(Kernel Mode Driver)。

UMD負責把應用API上的狀態設置(如MSAA,Blending,Depth Test,Texture等狀態設置)、Draw Call等轉化為硬體可識別的Command,並寫入Memory中的Command Buffer,寫滿了或在特殊情況下就提交:Submitting a Command Buffer(包括 Present, Flush, Lock),但這裡所謂提交,並非提交給GPU,而是提交給Kernel層的Scheduler。

因為每個應用可以擁有自身的UMD和各自提交的Command,需要Scheduler做調度,當輪到這個應用時,Scheduler就將之提交給KMD。KMD是真正跟GPU打交道的Driver

2)注意,KMD並不是直接把Command送給GPU來達成CPU與GPU的交流的,而是通過DMA。具體的講,KMD把要交給GPU的Command在Memory中的地址放在「DMA Command」,並把DMA Comand寫到Memory中一個叫Ring Buffer的結構中,Ring Buffer好比KMD和GPU溝通的橋樑,KMD和GPU最前端的Command Processor通過它構成生產/消費關係。一旦GPU消費得慢,Ring Buffer滿了,CPU就那邊會被阻塞。

GPU讀memory是高延遲的,通過DMA Command去memory讀數據會很漫長,Command Processor通常一次會請求一大把數據,讀回來的Command會寫入一個Command FIFO,Command Processor按順序解析每個Command,根據Command的類型(如CS或3D)或者給GPU配置State,或者發起通用計算,或者發起Draw任務,讓Pipeline所有模塊(Shader,Raster等)一同幹活。

3)關於Command Buffer的位置

Command Buffer可以放在System memory或Video memory,取決於具體硬體。放在的Video memory的話,KMD通過DMA傳送把DMA command寫到Video memory。

3)對於OpengGL而言,同步問題是Driver維護的,題主提到的程序員維護同步是指Vulkan?

4)至於

當cpu超過gpu 3幀,gpu還沒跑完當前指令,cpu就會阻塞等待

題主指的是否是下面的問題?這只是特殊情況,應用調用了需要同步的操作,一般不會說CPU等幾幀就阻塞。

2. 幀速率和時間查詢

關於幀速率,似乎以結果導向來看更準確,即單位時間內GPU出了多少張Image。不過個人覺得CPU跟GPU是生產者/消費關係,本身CPU扔一個Present,GPU就會消費之最終給出一張Image,時間稍微有點長時(從硬體角度看)兩者誤差不大(當然,具體我沒試驗過)。

關於時間,題主指的是GL_ARB_timer_query嗎?(http://developer.download.nvidia.com/opengl/specs/GL_ARB_timer_query.txt)

按NV的說明,測的是GPU的時間。

3. glutSwapBuffers/Present

應用調用一系列API或多個Draw call,GPU Pipeline最終把所有結果render在Framebuffer上,即framebuffer存儲著一張Image,glutSwapBuffers/Present是說——好,就畫這些,這一幀圖片就到此完成了。而Framebuffer通常有Back Buffer和Front Buffer兩個,避免只有一個Buffer時一邊畫一邊刷新屏幕的 tearing 問題——圖片一部分是舊幀,一部分是新幀的內容

Back Buffer是pipeline寫color的地方,Front Buffer則存著用於顯示到屏幕的數據,SwapBuffer時,Back Buffer用做Front Buffer,屏幕刷新後便顯示新的Image,而原來的Front Buffer則作為Back Buffer繼續給pipeline寫Image數據。


不打算寫太詳細,給幾個概念你自己搜索研究一下,會理解得更加深刻。以DX為例,因為Windows上架構和工具的關係,DX更有助於理解問題。


一個是fence,即作為command的邊界,又作為GPU command完成後通過interrupt通知CPU的ID。

一個是Windows上的gpuview,搜索一下用法,配合自己寫的dx sample看一下GPU hw queue以及對應app的CPU queue,command在queue里的順序以及關係。你還可以看到paging的動作,了解各種buffer何時被真正放入video memory。

command多少才會submit到GPU,runtime,dxglschedule策略,以及驅動策略,以及API層是不是顯式flush都有關係。如果考慮preemption,會更加複雜。如果還要從GPU HW角度考慮問題,那麼需要知道對GPU和CPU來說主要是兩類數據交換,第一是CPU為GPU準備各種resource,也就是buffer的過程,第二是CPU把command本身準備好由GPU fetch的過程。對於第一點,現在的dxgkrnl以及GPU driver會分配管理GPU地址空間,當你準備resource時,對應的GPU address,mem類型等各種屬性就已經準備好;對於第二點,一種是顯示的command,比如各種draw call,另一種是由dxgkrnl,dxgmms以及GPU driver根據resource需要進行調度的對上層不可見的command,比如搬memory的各種paging command,以及為了同步constant buffer的sync command等等,所有這些都會由GPU driver填充到GPU可以fetch到的command buffer queue中,由GPU中的command processor單元解析並處理分發。對於GPU實在太慢的情況,比如Windows上TDR,一條指令超過2s還沒返回就會觸發reset了。而scheduler也會處理command提交太快的情況,以免把command queue搞爆。

關於profiling,你要知道CPU與GPU的指令並非一起調度,任何你在CPU ISA里measure timing的動作都不能直接運用到GPU上,但還在GPU有各種interrupt,kernel里的dxgmms 和dxgkrnl以及GPU driver會處理各種GPU interrupt,從runtime角度看就是各種event,也就是你能在gpuview裡面能看到GPU execution time的根本原因。類似工具NV Nsight和Intel VTune都利用這種原理作profiling。

祝你早日搞清機理。


推薦閱讀:

對多重採樣(MSAA)原理的一些疑問?
如何用OpenGL封裝一個2D引擎?
如果做一個GIS渲染引擎,圖形API應當選擇OpenGL還是DirectX?
有哪些優秀的 C++ OpenGL 開源遊戲引擎?
為什麼 NVIDIA 的示例總是一條龍?

TAG:OpenGL | 圖形處理器GPU | DirectX | 遊戲編程 | 3D渲染 |