Win8/WinRTで、BlankApp(XAML)にコードを追加して、DXのSwapChainを表示するところまでやってみたいと思います。
合わせてTextBoxを一つだけ配置して、そこに文字を表示したいと思います。
(注:エラー処理、イベント処理等は省いております。極力最短でSwapChainBackgroundPanelでDXのレンダリングを表示するところまで行ったものです。)
SwapChainBackgroundPanelを配置する
まずはVisualStudio 2012でBlankApp(XAML)を作成します。
MainPage.xamlという、XMLファイルを編集します。DX11のSwapChainをXAMLのControlの一番下のレイヤーに表示してくれるSwapChainBackgroundPanelというControlがあるので、これを配置します。MainPage.xamlを以下のように変更します。ルートにSwapChainBackgroundPanelを配置して、子要素にテキスト表示用にTextBlockを配置します。
<SwapChainBackgroundPanel x:Name="DXSwapChainPanel" x:Class="App1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <TextBlock x:Name="FrameCountTB" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="58" Width="330"> </TextBlock> </SwapChainBackgroundPanel>
気を付けるところは、SwapChainBackgroundPanelのx:Classアトリビュートの部分です。ここは作成したプロジェクトの名前になっているので、適宜書き換える必要があります。また、この名前がXAMLから生成されるクラスの名前となります。
次にMainPage.xaml.cpp/hにある下記のメソッドを削除します。このメソッドはSwapChainBackgroundPanelには無いです。
virtual void OnNavigatedTo( Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override;
次にApp.xaml.cppのOnLaunchedメソッドを書き換えます。このメソッドはプロセス起動時に呼ばれるようです。SwapChainBackgroundPanelのクラスである、MainPageを配置するように書き換えます。
void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ args) { auto rootFrame = dynamic_cast<MainPage^>(Window::Current->Content); if (rootFrame == nullptr) { rootFrame = ref new MainPage(); Window::Current->Content = rootFrame; Window::Current->Activate(); } else { Window::Current->Activate(); } }
ここで一度ビルドして実行してみます。
正しくビルドできていれば、真っ黒な画面がでるStoreAppが完成するはずです。
DX11のSwapChainを作成する
次にSwapChainBackgroundPanelに実際にDX11のSwapChainを作成して接続してみます。
MainPage.xaml.hにインクルードを追加します。
#include "windows.ui.xaml.media.dxinterop.h" #include "D3D11_1.h"
MainPageクラスに下記のメンバーを追加します。
D3D_FEATURE_LEVEL m_featureLevel; Microsoft::WRL::ComPtr<ID3D11Device> m_device; Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_context; Microsoft::WRL::ComPtr<IDXGISwapChain1> m_swapChain; Microsoft::WRL::ComPtr<ISwapChainBackgroundPanelNative> m_swapChainNative; Microsoft::WRL::ComPtr<ID3D11RenderTargetView> m_RTView;
MainPageのコンストラクタにDXの初期化コードを追加します。通常のDXの初期化と違うのは、CreateSwapChainForCompositionを使ってSwapChainを作成しているところと、ISwapChainBackgroundPanelNative::SetSwapChainを使ってDXのSwapChainをSwapChainBackgroundPanelに設定している部分です。ここで行っているのは主に初期化の処理ですが、正しく動作しているかを確かめるためにRenderTargetを赤でクリアしてみます。
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 }; HRESULT hr; hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, 0, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &m_device, &m_featureLevel, &m_context); if (FAILED(hr)) { throw ref new FailureException("Failed to create device."); } DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = (UINT)Window::Current->Bounds.Width; swapChainDesc.Height = (UINT)Window::Current->Bounds.Height; swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapChainDesc.Stereo = false; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 2; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; swapChainDesc.Flags = 0; { Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice; m_device.As(&dxgiDevice); Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter; dxgiDevice->GetAdapter(&dxgiAdapter); Microsoft::WRL::ComPtr<IDXGIFactory2> dxgiFactory; dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), &dxgiFactory); hr = dxgiFactory->CreateSwapChainForComposition( m_device.Get(), &swapChainDesc, nullptr, &m_swapChain); if (FAILED(hr)) { throw ref new FailureException("Failed to create swap chain."); } } IInspectable* panelInspectable = (IInspectable*) reinterpret_cast<IInspectable*>(this); panelInspectable->QueryInterface( __uuidof(ISwapChainBackgroundPanelNative), (void **)&m_swapChainNative); m_swapChainNative->SetSwapChain(m_swapChain.Get()); Microsoft::WRL::ComPtr<ID3D11Texture2D> backBuffer; m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer)); D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc = {}; renderTargetViewDesc.Format = swapChainDesc.Format; renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; hr = m_device->CreateRenderTargetView( backBuffer.Get(), &renderTargetViewDesc, &m_RTView); ID3D11RenderTargetView *rtv= m_RTView.Get(); m_context->OMSetRenderTargets( 1, &rtv, NULL); const float cl[4] = {1.0f, 0.0f, 0.0f, 1.0f}; m_context->ClearRenderTargetView(m_RTView.Get(), cl); DXGI_PRESENT_PARAMETERS parameters = {}; hr = m_swapChain->Present1(1, 0, ¶meters); if (FAILED(hr)) { throw ref new FailureException("Failed to present."); }
ここまででビルドを行います。d3d11.libとdxgi.libをリンクするように設定します。 実行すれば赤い画面が表示されるはずです。これでDXのSwapChainを表示できたことになります。
レンダリングのイベントを登録する
このままでは、レンダリングを能動的に行うことができないので、レンダリングを行うためのイベントを登録します。まずは、イベントハンドラになるメソッドをMainPageに追加します。これが毎フレーム呼び出されるメソッドになります。
void MainPage::OnRendering(_In_ Object^ sender, _In_ Object^ args) { static int cnt; const float cl[4] = {(float)(cnt++%255)/255.0f, 0, 0, 1}; m_d3dContext->ClearRenderTargetView(m_d3dRenderTargetView.Get(), cl); DXGI_PRESENT_PARAMETERS parameters = {}; parameters.DirtyRectsCount = 0; parameters.pDirtyRects = nullptr; parameters.pScrollRect = nullptr; parameters.pScrollOffset = nullptr; m_swapChain->Present1(1, 0, ¶meters); }
次にMainPageに以下のメンバーを追加します。
Windows::Foundation::EventRegistrationToken m_onRenderingEventToken;
MainPageのコンストラクタの一番最後でイベントの登録を行います。
m_onRenderingEventToken = CompositionTarget::Rendering::add(ref new EventHandler(this, &MainPage::OnRendering));
これで、毎フレームOnRenderingが呼び出されるようになります。OnRendering内でRTのクリアカラーを変化させているので、ビルドして実行すれば赤が点滅するようになるはずです。
テキストボックスに文字を表示する
最後に、MainPage.xamlに配置しておいたTextBoxにOnRenderingで使用してるフレームカウンタの値を表示してみます。ここはDXとは関係ありません。
MainPage.xaml.cppにインクルードを追加します。
#include "Strsafe.h"
OnRenderの最後に以下のコードを追加します。
char16 strWrk[256]; StringCbPrintfW(strWrk, 255, L"%d", cnt); FrameCountTB->Text = ref new Platform::String(strWrk);
FrameCountTBはMainPage.xaml内のTextBoxのx:nameアトリビュートで指定した名前です。そこにテキストを設定しています。
ビルドして実行すれば、フレームカウンタが左上に表示されれるはずです。