Vulkan與DX11交互

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紋理結果拿出來渲染,現在移動窗口正常了。

Tags: