共享紋理
老遊戲使用directx9無法直接與cc高光sdk(d3d11)對接,但是d3d9ex有共享紋理,我們透過共享紋理把遊戲畫面共享給cc錄製,記錄一些踩坑的筆記。
共享紋理示例:
// 初始化Direct3D
void initD3D9(HWND hWnd)
{
hr = d3d9exdev->GetRenderTarget(0, &g_d3d9RenderSurface);
D3DSURFACE_DESC desc;
g_d3d9RenderSurface->GetDesc(&desc);
//關於格式說明:影像的格式必須與desc.format的格式一致,否則共享紋理的畫面是黑色的,並且pool要使用default
hr = d3d9exdev->CreateOffscreenPlainSurface(desc.Width, desc.Height, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT,
&g_d3d9SharedSurface, &g_d3d9SharedHandle);
if (FAILED(hr))
{
OutputDebugStringA("CreateOffscreenPlainSurface failed\n");
}
}
// 渲染一幀
void RenderD3D9(void)
{
hr = d3d9exdev->StretchRect(g_d3d9RenderSurface, NULL, g_d3d9SharedSurface, NULL, D3DTEXF_NONE);
if (FAILED(hr))
{
OutputDebugStringA("GetRenderTargetData failed\n");
}
}
//g_d3d9SharedHandle就是共享紋理的控制代碼,傳給sdk進行錄製
long long sharedHandleAddress = reinterpret_cast<long long>(g_d3d9SharedHandle);
建立d3d9ex
只有d3d9ex才能共享紋理,d3d9無法共享紋理
LPDIRECT3D9EX d3d9ex = nullptr;
Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9ex);
VS中正常,編譯出的exe出錯
在與cc聯調的過程中碰到一個問題:從vs中啟動共享紋理正常,而打包出來的exe共享紋理是黑色的。
最後透過各種排查,各種換方法,才定位到問題,我們遊戲載入的d3d9.dll非微軟原版的!
把我們遊戲的d3d9.dll,發給CC測試發現:遊戲程序CreateOffscreenPlainSurface建立的紋理在錄製sdk無法開啟
那為什麼從vs裡啟動就是正常的呢?
在vs中啟動從模組視窗中可以看到載入的是系統的dll
編譯出來的exe相同dll載入的位置已經變了!
嘗試刪掉或替換遊戲目錄下的d3d9.dll,測試結果如下表:
d3d9.dll | 獨立程序 | 同程序 |
---|---|---|
替換d3d9.dll為微軟原版 | √ | √ |
刪除d3d9.dll | √ | √ |
windows dll載入的順序
打包應用系統按以下順序搜尋:
- DLL 重定向。
- API 集。
- 桌面應用僅 (UWP 應用) 。 SxS 清單重定向。
- Loaded-module 列表。
- 已知 DLL。
- 程序的包依賴項關係圖。 這是應用程式的包,以及應用程式包清單的 節
中指定的任何依賴項 。 依賴項按它們在清單中的出現順序進行搜尋。 - 呼叫程序從載入的資料夾 (可執行檔案的資料夾) 。
- 系統資料夾 (%SystemRoot%\system32) 。
附官方文件:Dynamic-link library search order - Win32 apps | Microsoft Learn
為什麼要替換微軟的d3d9.dll?
我們有部分集顯玩家的地表會出問題,所以遊戲內有個設定使用vulkan,勾選後會使用dxvk,不過這個dll也被命名為d3d9.dll,但是在內服為了截幀所以預設替換了遊戲目錄下的d3d9.dll
doitsujin/dxvk: Vulkan-based implementation of D3D9, D3D10 and D3D11 for Linux / Wine (github.com)
關閉編譯最佳化導致錄影閃屏
在我本地打包出來的遊戲錄影偶爾閃爍,而QA跑打包機打出來的遊戲則錄影影片特別閃,遊戲內畫面正常只有錄影出現閃屏。
經過對比發現,我本地有兩個工程的vs c++編譯最佳化關閉了,也就是:專案 - 屬性 - C/C++ - 最佳化:【最大最佳化O2】改成【已禁用】
於是嘗試開啟我本地工程的c++最佳化,果然影片也會閃屏,那為什麼這個最佳化會導致閃屏呢?
因為關閉最佳化後遊戲執行速度變慢,遊戲變慢就變成同步了從而掩蓋了共享紋理的問題。而遊戲是多執行緒的,對紋理的訪問是非同步的就會閃爍。
最終解決辦法
Moo::rc().device();獲取D3D裝置,而不是遊戲初始化透過CreateDeviceEx建立的pD3D9Ex,因為引擎裡對D3D9Ex進行了封裝,預設就開啟了D3D9Ex,並且 Moo::rc().device()處理了多執行緒的同步問題
HRESULT DXUTCreate3DEnvironment9( IDirect3DDevice9* pd3dDeviceFromApp )
{
hr = pD3D9Ex->CreateDeviceEx( pNewDeviceSettings->d3d9.AdapterOrdinal, pNewDeviceSettings->d3d9.DeviceType, \
DXUTGetHWNDFocus(), pNewDeviceSettings->d3d9.BehaviorFlags, \
&pNewDeviceSettings->d3d9.pp, NULL, &pd3dDevice9Ex);
}
使用process explorer檢視程式的控制代碼和載入的dll
選單點選 View - Lower Pane View - 勾選DLLS和Handles ,並且勾選 View - Show Lower Pane,然後選中某個程序後,在底部就會出現dll tab頁,裡面顯示了當前程序載入了那些dll