2016-04-25 20 views
1

我的目標是讀取默認OpenGL幀緩衝區的內容並將像素數據存儲在cv::Mat中。顯然有2種實現這一目標的不同方式:從OpenGL的默認幀緩衝區中讀取像素數據:FBO與PBO的性能

1)同步:使用FBO和glRealPixels

cv::Mat a = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3); 
glReadPixels(0, 0, 1920, 1080, GL_BGR, GL_UNSIGNED_BYTE, a.data); 

2)異步:使用PBO和glReadPixels

cv::Mat b = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3); 
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_userImage); 
    glReadPixels(0, 0, 1920, 1080, GL_BGR, GL_UNSIGNED_BYTE, 0); 
    unsigned char* ptr = static_cast<unsigned char*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY)); 
    std::copy(ptr, ptr + 1920 * 1080 * 3 * sizeof(unsigned char), b.data); 
    glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 

根據全部信息我收集了關於這個主題的異步版本2)應該快得多。但是,比較兩種版本的使用時間可以得出差異通常是最小的,有時版本1)的事件優於PBO變體。

出於性能方面的檢查,我已經插入下面的代碼(基於this答案):

std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 
.... 
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 
std::cout << "Time difference = " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl; 

我也試驗了使用提示創建PBO時:我沒找到這裏的GL_DYNAMIC_COPYGL_STREAM_READ之間的差別很大。

我很樂意提供如何進一步提高幀緩衝器中像素讀取操作速度的建議。

+1

你的第二個版本並不是非同步的,因爲你在調用'glReadPixels()'調用之後立即阻塞結果。 –

+0

你的意思是調用'std :: copy'?實際上,如果我評論這條線,效果會很小,而版本1有時會更快。 – Schnigges

+0

如果我不將GPU緩衝區映射到CPU內存,這當然是預料之中的,但在此之後必須有相當大的不同,因爲我想將'cv :: Mat'存儲在向量中 – Schnigges

回答

5

您的第二個版本根本不是異步的,因爲您在觸發複製後立即映射緩衝區。然後地圖調用將阻塞,直到緩衝區的內容可用,實際上變爲同步。

或者:根據驅動程序的不同,在實際讀取時會阻止。換句話說,驅動程序可能會以導致頁面錯誤和後續同步的方式實現映射。在你的情況下,這並不重要,因爲由於std::copy,你仍然在直接訪問該數據。

正確的方法這樣做是通過使用sync objects and fences

保持您的PBO設置,但在將glReadPixels發佈到PBO後,通過glFenceSync將同步對象插入到流中。然後,一段時間後,通過glClientWaitSync輪詢該柵欄同步對象完成(或者完全等待)。

如果glClientWaitSync返回fence之前的命令完成,現在可以從緩衝區中讀取而不需要昂貴的CPU/GPU同步。 (如果驅動程序特別愚蠢並且尚未將緩衝區內容移動到可映射地址中,則儘管您在PBO上使用了提示,但您可以使用另一個線程來執行映射。因此,glGetBufferSubData可能會更便宜,因爲數據不會「T需要在一個可映射範圍內。)


如果你需要做這個一幀一幀的基礎上,你會發現,它很可能是你需要一個以上的PBO ,也就是說,他們有一小部分。這是因爲在下一幀,前一幀數據的回讀尚未完成,並且未發送相應的柵欄。 (是的,GPU現在是大規模流水線的,並且它們將會在您的提交隊列後面有一些幀)。

+0

我想補充說,如果您在觸發操作('glReadPixels'和PBO)和讀取結果('glMapBuffer','glGetBufferSubData'等)之間做某些事情,則異步執行傳輸只會帶來益處。創建一個同步對象並立即等待就不會比直接使用'glReadPixels'更好。 –

相關問題