D3DImageとDirect2Dデバイスコンテキストを組み合わせるコードが全然見当たらなかったので、試しに書いてみました。
WPFのD3DImageは、そのままではDirect2Dでの描画を表示できるようにはできていません。
- D3DImageはDirect3D 9サーフェースしか受け付けない。
- Direct2DはDXGIサーフェース(つまりDirect3D 10以上)を使う。
対策として、Direct3D 9Exから追加された共有ハンドル (shared handle)を使います。これを使えば、Direct3D/DXGIのバージョンの壁を越えて、共有のリソースが作れます。今回はサーフェースを共有します。
ATL::CComPtr<IDirect3DDevice9Ex> d3d9Device; // IDirect3D9Ex::CreateDeviceExでDirect3D 9Exデバイスを作っておく。 ATL::CComPtr<IDirect3DSurface9> renderTarget; HANDLE hSharedResource = nullptr; Marshal::ThrowExceptionForHR(d3d9Device->CreateRenderTarget( WIDTH, HEIGHT, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &renderTarget, &hSharedResource)); ATL::CComPtr<ID3D11Device> d3d11Device; // D3D11CreateDeviceでDirect3D 11デバイスを作っておく。 ATL::CComPtr<IDXGISurface2> dxgiSurface; Marshal::ThrowExceptionForHR(d3d11Device->OpenSharedResource( hSharedResource, IID_PPV_ARGS(&dxgiSurface))); // dxgiSurfaceに対して、Direct2Dで描画する。 |
書いていて気付いたことなどです。
- 今回は、OpenSharedResourceの結果をIDXGISurface2で受け取っていますが、ID3D11Texture2Dとしても受け取れると思います。
- 描画後(ID2D1DeviceContext::EndDraw呼び出し後)にID3D11DeviceContext::Flushを呼び出す必要があります。MSDNライブラリのID3D11Device::OpenSharedResourceにも書いてありますね。
- D3D11CreateDeviceにおいてD3D_DRIVER_TYPE_WARPではダメでした。そのため、D3D_DRIVER_TYPE_HARDWAREのみとしています。
以下、コード全体です。C++/CLIです。お試しのつもりだったので、wWinMain関数に直接いろいろ書いています。
// cl /clr /EHa /MD wpfd2d.cpp /AI ^ // "%ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6" #include <cstdlib> #include <windows.h> #include <d3d9.h> #include <d3d11_1.h> #include <dxgi1_2.h> #include <d2d1_2.h> #include <atlbase.h> #using <System.Xaml.dll> #using <WindowsBase.dll> #using <PresentationCore.dll> #using <PresentationFramework.dll> #pragma comment(lib, "d3d9.lib") #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d2d1.lib") using namespace System; using namespace System::Runtime::InteropServices; using namespace System::Windows; using namespace System::Windows::Controls; using namespace System::Windows::Interop; constexpr int WIDTH = 800; constexpr int HEIGHT = 600; void OnRenderImageCore(_Inout_ ID2D1DeviceContext* d2dDeviceContext) { d2dDeviceContext->BeginDraw(); d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::DarkGreen)); ATL::CComPtr<ID2D1SolidColorBrush> brush; d2dDeviceContext->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::White), &brush); // 試しに円を描いてみる。 D2D1_ELLIPSE ellipse{ { WIDTH * 0.5f, HEIGHT * 0.5f }, WIDTH * 0.5f, HEIGHT * 0.5f, }; d2dDeviceContext->DrawEllipse(ellipse, brush, 10.f); D2D1_ELLIPSE ellipseS{ { 100.f, 100.f }, 100.f, 100.f, }; d2dDeviceContext->DrawEllipse(ellipseS, brush, 10.f); Marshal::ThrowExceptionForHR(d2dDeviceContext->EndDraw()); } [STAThread] int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int) { auto w = gcnew Window; auto image = gcnew Image; auto d3dImage = gcnew D3DImage; image->Source = d3dImage; image->Width = WIDTH; image->Height = HEIGHT; w->SizeToContent = SizeToContent::WidthAndHeight; w->ResizeMode = ResizeMode::CanMinimize; w->Content = image; // 強制的にウィンドウを作る効果がある。 // この次にウィンドウハンドルを取得するため、ウィンドウの作成が必要。 w->Show(); auto hwnd = static_cast<HWND>(safe_cast<HwndSource^>( PresentationSource::FromVisual(w))->Handle.ToPointer()); ATL::CComPtr<IDirect3D9Ex> d3d9; Marshal::ThrowExceptionForHR(Direct3DCreate9Ex(DIRECT3D_VERSION, &d3d9)); ATL::CComPtr<IDirect3DDevice9Ex> d3d9Device; D3DPRESENT_PARAMETERS d3dpp{}; d3dpp.BackBufferWidth = 800; d3dpp.BackBufferHeight = 600; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = TRUE; Marshal::ThrowExceptionForHR(d3d9->CreateDeviceEx( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_DISABLE_PRINTSCREEN, &d3dpp, nullptr, &d3d9Device)); ATL::CComPtr<IDirect3DSurface9> renderTarget; HANDLE hSharedResource = nullptr; // 最初D3DFMT_X8R8G8B8にしたら、Direct2Dのところでエラーになった Marshal::ThrowExceptionForHR(d3d9Device->CreateRenderTarget( WIDTH, HEIGHT, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, FALSE, &renderTarget, &hSharedResource)); ATL::CComPtr<ID3D11Device> d3d11Device; ATL::CComPtr<ID3D11DeviceContext> d3d11ImmediateContext; UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; const D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1, }; Marshal::ThrowExceptionForHR(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &d3d11Device, nullptr, &d3d11ImmediateContext)); ATL::CComQIPtr<IDXGIDevice2> dxgiDevice = d3d11Device; ATL::CComPtr<IDXGISurface2> dxgiSurface; Marshal::ThrowExceptionForHR(d3d11Device->OpenSharedResource( hSharedResource, IID_PPV_ARGS(&dxgiSurface))); ATL::CComPtr<ID2D1Factory1> d2dFactory; D2D1_FACTORY_OPTIONS options = {}; #ifdef _DEBUG options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; #endif Marshal::ThrowExceptionForHR(D2D1CreateFactory( D2D1_FACTORY_TYPE_MULTI_THREADED, options, &d2dFactory)); ATL::CComPtr<ID2D1Device> d2dDevice; Marshal::ThrowExceptionForHR(d2dFactory->CreateDevice( dxgiDevice, &d2dDevice)); ATL::CComPtr<ID2D1DeviceContext> d2dDeviceContext; Marshal::ThrowExceptionForHR(d2dDevice->CreateDeviceContext( D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &d2dDeviceContext)); ATL::CComPtr<ID2D1Bitmap1> d2d1RenderTarget; Marshal::ThrowExceptionForHR(d2dDeviceContext->CreateBitmapFromDxgiSurface( dxgiSurface, D2D1::BitmapProperties1( D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)), &d2d1RenderTarget)); d2dDeviceContext->SetTarget(d2d1RenderTarget); OnRenderImageCore(d2dDeviceContext); d3d11ImmediateContext->Flush(); if (d3dImage->IsFrontBufferAvailable) { d3dImage->Lock(); d3dImage->SetBackBuffer( D3DResourceType::IDirect3DSurface9, IntPtr(renderTarget)); d3dImage->AddDirtyRect( Int32Rect(0, 0, d3dImage->PixelWidth, d3dImage->PixelHeight)); d3dImage->Unlock(); } auto app = gcnew Application; std::quick_exit(app->Run(w)); } |
C++/CLIだとWRLが使えないんですね。Microsoft::WRL::ComPtrを使おうとしたら、インクルードしただけで#errorで引っかかるようになっていました。そのため、ATL::CComPtrを使っています。
以上、Direct2D 1.1→DXGI 1.2 (Direct3D 11.1)→Direct3D 9Ex→D3DImageという組み合わせのサンプルコードでした。
スポンサード リンク |