HTC ViveでペンタVR上でクリスタが使えるように改良する その3(DesktopCaptureが頻繁に落ちるのを修正する)
前回
その1 HTC ViveでペンタVR上でクリスタが使えるように改良する - Unityシェーダ入門
その2 HTC ViveでペンタVR上でクリスタが使えるように改良する その2(解像度問題の解決策を模索する編) - Unityシェーダ入門
はじめに
前回までは Unity で Desktop Duplication API を使ってスクリーンキャプチャしてみた - 凹みTips で配布されているパッケージ内のDLLをそのまま使用していましたが、何か操作するたびに(動画のシークやウィンドウの最大化など)頻繁に落ちていました。
Desktop Duplication API (Windows) を流し読みして調べたところ、AcquireNextFrame IDXGIOutputDuplication::AcquireNextFrame method (Windows)は結構色々なエラーコードを返してくるようです。
前回までのソースコードを見る限りエラー処理はされていないので、そこで落ちてるのかなと見当をつけました。
手順
- VisualStudio2015でファイルの新規作成->プロジェクト
- VisualC++ のWin32のWin32 Console Applicationを選択(名前はDesktopCaptureにしておく)
- ウィザードでDLLを選択、空のプロジェクトをチェックして完了
- Source Filesにcppファイルを新規作成
- cppファイルにソースコードをコピー
- ビルドターゲットをDebug->Release,x86 -> x64に変更
- Unity - Manual: Low-level Native Plugin Interfaceからヘッダーファイルのzipを落として伸長
- \RenderingPluginExample53\RenderingPlugin\Unity内の、IUnityGraphics.hとIUnityGraphicsD3D11.hとIUnityInterface.hを、作ったプロジェクトのフォルダ内(さっき作ったcppがあるフォルダ)にコピー
- Header Filesに追加->既存の項目で上記3ファイルを選択
- ビルドするとDesktopCapture.dllが出てくるので、既にインポートしてあるDesktopCapture.dllと差し替える
以下ソースコード。
エラーコードDXGI_ERROR_ACCESS_LOSTのみ、g_deskDupl(IDXGIOutputDuplication)を生成し直す必要があると書いてあるため、対処している。
#include <d3d11.h> #include <dxgi1_2.h> #include "IUnityInterface.h" #include "IUnityGraphics.h" #include "IUnityGraphicsD3D11.h" #pragma comment(lib, "dxgi.lib") namespace { IUnityInterfaces* g_unity = nullptr; IDXGIOutputDuplication* g_deskDupl = nullptr; ID3D11Texture2D* g_texture = nullptr; bool g_isPointerVisible = false; int g_pointerX = -1; int g_pointerY = -1; int g_width = -1; int g_height = -1; } extern "C" { void CreateDuplicatation() { IDXGIFactory1* factory; CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory)); // 全ディスプレイアダプタを調べる IDXGIAdapter1* adapter; for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i) { // アウトプットを一通り調べてメインモニタを探す IDXGIOutput* output; for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); j++) { DXGI_OUTPUT_DESC outputDesc; output->GetDesc(&outputDesc); MONITORINFOEX monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFOEX); GetMonitorInfo(outputDesc.Monitor, &monitorInfo); if (monitorInfo.dwFlags == MONITORINFOF_PRIMARY) { g_width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; g_height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice(); IDXGIOutput1* output1; output1 = reinterpret_cast<IDXGIOutput1*>(output); output1->DuplicateOutput(device, &g_deskDupl); output->Release(); adapter->Release(); factory->Release(); return; } output->Release(); } adapter->Release(); } factory->Release(); } UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces) { g_unity = unityInterfaces; CreateDuplicatation(); } UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload() { g_unity = nullptr; g_deskDupl->Release(); g_deskDupl = nullptr; g_texture = nullptr; g_isPointerVisible = false; g_width = -1; g_height = -1; g_pointerX = -1; g_pointerY = -1; } void UNITY_INTERFACE_API OnRenderEvent(int eventId) { if (g_deskDupl == nullptr || g_texture == nullptr) return; IDXGIResource* resource = nullptr; DXGI_OUTDUPL_FRAME_INFO frameInfo; const UINT timeout = 500; // ms HRESULT hr = g_deskDupl->AcquireNextFrame(timeout, &frameInfo, &resource); // 作り直し if (hr == DXGI_ERROR_ACCESS_LOST) { g_deskDupl->Release(); g_deskDupl = nullptr; CreateDuplicatation(); } // 失敗 if (hr != S_OK) { return; } g_isPointerVisible = frameInfo.PointerPosition.Visible; g_pointerX = frameInfo.PointerPosition.Position.x; g_pointerY = frameInfo.PointerPosition.Position.y; ID3D11Texture2D* texture; resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture)); resource->Release(); ID3D11DeviceContext* context; auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice(); device->GetImmediateContext(&context); context->CopyResource(g_texture, texture); g_deskDupl->ReleaseFrame(); } UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc() { return OnRenderEvent; } UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth() { return g_width; } UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight() { return g_height; } UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API IsPointerVisible() { return g_isPointerVisible; } UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerX() { return g_pointerX; } UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerY() { return g_pointerY; } UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(void* texture) { g_texture = reinterpret_cast<ID3D11Texture2D*>(texture); } }
まとめ
今までなら確実に落ちていた、メインウィンドウ以外でのUnityエディタ実行や、動画のシーク、CLIP STUDIO PAINTの最大化なども落ちなくなりました。
(クリスタの最大化などは、落ちることはないんですが点滅したりします)
ちなみに拡大鏡機能ですが、ムフフ動画を見ているときなんかに一部分をズームしたりできるので、なんだか楽しいです!
実行形式も置いておくので、暇な人は試してみてください(さすがにペンタVRとするわけにはいかないので、ペンタViveにしました!)
https://dl.dropboxusercontent.com/u/147606158/PentaVive.zip