Vulkan與DX11互動

天天不在發表於2021-02-08

Demo演示地址07_wintest

有什麼用

在android平臺主流是用opengl es,android下vulkan與opengles紋理互通。 而在win平臺,主流遊戲還用的是DX11,如果只是把結果通過CPU的記憶體輸出,然後接到dx11,這個效能損失太大了,我們就需要Vulkan與DX11互動。 以及完成這個互動後,aoce我就不做dx11模組了,我比較了oeip中的dx11模組與當前的vulkan模組效能相差不大,都比CUDA差,但是通用性更好。

主要實現

如果有興趣,可以看下aoce_vulkan/win32/VkWinImage類的實現,dx11與vulkan繫結的邏輯主要在這。

與opengles互動類似,資料不多,主要看到vulkan下有個vulkan_win32的標頭檔案,看到如VkImportMemoryWin32HandleInfoKHR這些結構,用google搜下,可以看到BindImageMemory有比較完整的互動邏輯,主要就是用DX11Texture共享紋理,注意這裡要用NT控制程式碼,就是相關MiscFlags需要包含D3D11_RESOURCE_MISC_SHARED_NTHANDLE,而在cuda/dx11互動裡用cudaGraphicsD3D11RegisterResource用NT控制程式碼 會失敗,所以最好用個標誌表示是否需要NT控制程式碼,NT控制程式碼需要自己CreateSharedHandle,從NT控制程式碼得到相應shader buffer需要通過ID3D11Device1拿到,餘下的邏輯和非NT控制程式碼差不多了,相關程式碼aoce_win/DX11/Dx11SharedTex檢視具體實現。

然後就是按照cuda/dx11互動那樣,vulkan最後輸出結果到繫結dx11texture上的那個vkImage,然後在dx11渲染的另外一個執行緒把上面的dx11texture結果輸出來就行,想的應該是這樣,然後就開始不斷啟動就報device lost,然後啟動幾次後機器卡死/當機藍屏,最開始我想的肯定是同步問題,繼續在vulkan_win32的標頭檔案找,找到如下VkWin32KeyedMutexAcquireReleaseInfoKHR結構,這個結構不就是 dx11不同執行緒互動的同步API的AcquireSync/ReleaseSync,根據這個結構搜尋到dx11-vulkan-keymutex根據這裡的邏輯改下,然後發現還是卡死/當機,我開始根據新增加程式碼一行行遮蔽測試,不斷當機/藍屏,最後我忽然想到解決動態啟用/關閉層時遇到的一個問題,其中把運算結果複製給繫結dx11資源的vkImage,用的是vkCmdBlitImage,改成vkCmdCopyImage,然後問題解決,以前我因為vkCmdBlitImage裡源和目標紋理不需要同樣大小就一直用的這個,我猜測這個API應該是需要渲染管線與交換鏈那一套的, 在這裡我只有計算管線所以會導致問題,後面有時間驗證下這個問題。

這個問題解決後,可以正常執行了,但是,你不動視窗執行多久沒問題,但是一動視窗vulkan就報timeout,而這timeout一看就是VkWin32KeyedMutexAcquireReleaseInfoKHR上面的, 我猜測在移動視窗時,導致繫結dx11texture上的那個vkImage那個資源一直被dx11渲染佔用著,所以就有這個問題,而我設計輸出層時,設計要求執行執行緒與輸出執行緒沒有等待關係,二個執行緒可以分別以自己楨率執行 ,就和我在cuda互動裡的處理,設定timeout為0,檢查鎖,如果鎖timeout,就馬上放棄複製,執行緒繼續執行,而在這,我並不能通過這個介面實現這種邏輯。

最後想了想,vulkan執行執行緒中,我可以用vkFence知道是否在執行commandbuffer,那麼在二次執行中先複製結果一個臨時dx11紋理中, 這樣也不需要針對這個臨時dx11紋理與vulkan執行執行緒同步,把原來的繫結dx11texture上的那個vkImage的MiscFlags中的D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX改成D3D11_RESOURCE_MISC_SHARED,然後把相關的 VkWin32KeyedMutexAcquireReleaseInfoKHR程式碼去掉,vulkan執行comandbuffer執行完成後,使用vkFence等待,等待完成後把繫結dx11texture上的那個vkImage輸出到臨時dx11紋理中。

最後在Dx11的渲染執行緒中,把臨時dx11紋理結果拿出來渲染,現在移動視窗正常了。

相關文章