SwapChainBackgroundPanelを使ってみる

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, &parameters);
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, &parameters);
}

次に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アトリビュートで指定した名前です。そこにテキストを設定しています。
ビルドして実行すれば、フレームカウンタが左上に表示されれるはずです。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中