アヒルのある日

株式会社AHIRUの社員ブログです。毎週更新!社員が自由に思いついたことを書きます。

DX12との格闘 #2

こんにちは、ちゃらいプログラマです。
今回はテクスチャを表示してみたいと思います。

※前回の記事はこちら blog.ahiru.co.jp

準備

png形式の画像を表示したかったのでWIC(Windows Imaging Component)を使用しています。

github.com

上記から

  • DirectXTex/d3dx12.h
  • WICTextureLoader/WICTextureLoader12.cpp
  • WICTextureLoader/WICTextureLoader12.h

をそれぞれダウンロードし、プロジェクトに追加してください。

インクルード追加

#include "d3dx12.h"
#include "WICTextureLoader12.h"

頂点構造体の変更

以下のように定義を変更しています。

struct Vertex
{
    XMFLOAT3    _position;
    XMFLOAT3    _normal;
    XMFLOAT2    _uv;
};

ルートシグネチャの生成変更

定数バッファ設定とテクスチャ設定を適用するように変更しています。

// ルートシグネチャ生成
{
    D3D12_DESCRIPTOR_RANGE      rangeArray[1];
    D3D12_ROOT_PARAMETER        rootParameterArray[2];
    D3D12_STATIC_SAMPLER_DESC   samplerDesc{};

    // 定数バッファをデスクリプタを介さずに渡すように設定
    rootParameterArray[0].ParameterType                         = D3D12_ROOT_PARAMETER_TYPE_CBV;
    rootParameterArray[0].Descriptor.ShaderRegister             = 0;
    rootParameterArray[0].Descriptor.RegisterSpace              = 0;
    rootParameterArray[0].ShaderVisibility                      = D3D12_SHADER_VISIBILITY_ALL;

    // テクスチャのデスクリプタヒープを渡すように設定
    rangeArray[0].RangeType                                     = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
    rangeArray[0].NumDescriptors                                = 1;
    rangeArray[0].BaseShaderRegister                            = 0;
    rangeArray[0].RegisterSpace                                 = 0;
    rangeArray[0].OffsetInDescriptorsFromTableStart             = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;

    rootParameterArray[1].ParameterType                         = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
    rootParameterArray[1].ShaderVisibility                      = D3D12_SHADER_VISIBILITY_ALL;
    rootParameterArray[1].DescriptorTable.NumDescriptorRanges   = 1;
    rootParameterArray[1].DescriptorTable.pDescriptorRanges     = &rangeArray[0];

    // サンプラ設定
    samplerDesc.Filter                  = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
    samplerDesc.AddressU                = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
    samplerDesc.AddressV                = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
    samplerDesc.AddressW                = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
    samplerDesc.MipLODBias              = 0.f;
    samplerDesc.MaxAnisotropy           = 16;
    samplerDesc.ComparisonFunc          = D3D12_COMPARISON_FUNC_NEVER;
    samplerDesc.BorderColor             = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
    samplerDesc.MinLOD                  = 0.f;
    samplerDesc.MaxLOD                  = D3D12_FLOAT32_MAX;
    samplerDesc.ShaderRegister          = 0;
    samplerDesc.RegisterSpace           = 0;
    samplerDesc.ShaderVisibility        = D3D12_SHADER_VISIBILITY_ALL;

    // ルートシグネチャ生成
    D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
    rootSignatureDesc.NumParameters             = _countof(rootParameterArray);
    rootSignatureDesc.pParameters               = rootParameterArray;
    rootSignatureDesc.NumStaticSamplers         = 1;
    rootSignatureDesc.pStaticSamplers           = &samplerDesc;
    rootSignatureDesc.Flags                     = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;

    ComPtr<ID3DBlob> signature    = nullptr;
    ComPtr<ID3DBlob> error        = nullptr;

    if( FAILED( D3D12SerializeRootSignature( &rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error ) ) )
    {
        return false;
    }
    if( FAILED( g_device->CreateRootSignature( 0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&g_rootSignature) ) ) )
    {
        return false;
    }
}

頂点レイアウトの変更

頂点構造体の変更に合わせて以下のように変更しています。

// 頂点入力レイアウト定義
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = {
    {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    {"NORMAL"  , 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
    {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT   , 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
};

ブレンドの変更

αブレンドに変更しています。

{
    // ブレンド
    D3D12_BLEND_DESC blendDesc          = {};
    blendDesc.AlphaToCoverageEnable     = false;
    blendDesc.IndependentBlendEnable    = false;
    for( UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i ++ )
    {
        blendDesc.RenderTarget[i].BlendEnable           = true;
        blendDesc.RenderTarget[i].LogicOpEnable         = false;
        blendDesc.RenderTarget[i].SrcBlend              = D3D12_BLEND_SRC_ALPHA;
        blendDesc.RenderTarget[i].DestBlend             = D3D12_BLEND_INV_SRC_ALPHA;
        blendDesc.RenderTarget[i].BlendOp               = D3D12_BLEND_OP_ADD;
        blendDesc.RenderTarget[i].SrcBlendAlpha         = D3D12_BLEND_ONE;
        blendDesc.RenderTarget[i].DestBlendAlpha        = D3D12_BLEND_ZERO;
        blendDesc.RenderTarget[i].BlendOpAlpha          = D3D12_BLEND_OP_ADD;
        blendDesc.RenderTarget[i].LogicOp               = D3D12_LOGIC_OP_NOOP;
        blendDesc.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
    }
    gpsDesc.BlendState = blendDesc;
}

頂点バッファの定義変更

頂点構造体の変更に合わせて以下のように変更しています。

// ジオメトリ定義
Vertex vertexArray[] = {
    {{ -1.f,  1.f, 0.f}, {0.f, 0.f, -1.f}, {0.f, 0.f}},
    {{  1.f,  1.f, 0.f}, {0.f, 0.f, -1.f}, {1.f, 0.f}},
    {{  1.f, -1.f, 0.f}, {0.f, 0.f, -1.f}, {1.f, 1.f}},
    {{ -1.f, -1.f, 0.f}, {0.f, 0.f, -1.f}, {0.f, 1.f}}
};

インデックスバッファ、定数バッファ、テクスチャの生成

今回から四角形を描画する際にインデックスバッファを使用しているのと、テクスチャ情報を読み込んでシェーダーリソースビューを生成しています。

// インデックスバッファ生成
{
    uint32_t indices[] = { 0, 1, 2, 0, 2, 3 }; // 描画順

    auto indexBufferSize = sizeof(indices);

    D3D12_HEAP_PROPERTIES heapProperties    = {};
    heapProperties.Type                     = D3D12_HEAP_TYPE_UPLOAD;
    heapProperties.CPUPageProperty          = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    heapProperties.MemoryPoolPreference     = D3D12_MEMORY_POOL_UNKNOWN;
    heapProperties.CreationNodeMask         = 1;
    heapProperties.VisibleNodeMask          = 1;

    D3D12_RESOURCE_DESC resourceDesc        = {};
    resourceDesc.Dimension                  = D3D12_RESOURCE_DIMENSION_BUFFER;
    resourceDesc.Alignment                  = 0;
    resourceDesc.Width                      = indexBufferSize;
    resourceDesc.Height                     = 1;
    resourceDesc.DepthOrArraySize           = 1;
    resourceDesc.MipLevels                  = 1;
    resourceDesc.Format                     = DXGI_FORMAT_UNKNOWN;
    resourceDesc.SampleDesc.Count           = 1;
    resourceDesc.SampleDesc.Quality         = 0;
    resourceDesc.Layout                     = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    resourceDesc.Flags                      = D3D12_RESOURCE_FLAG_NONE;

    if( FAILED( g_device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&g_indexBuffer) ) ) )
    {
        return false;
    }

    // データ設定
    UINT8* pIndexDataBegin;
    if ( FAILED( g_indexBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&pIndexDataBegin) ) ) )
    {
        return false;
    }
    memcpy( pIndexDataBegin, indices, indexBufferSize );
    g_indexBuffer->Unmap( 0, nullptr );

    // ビュー初期化
    g_indexBufferView.BufferLocation    = g_indexBuffer->GetGPUVirtualAddress();
    g_indexBufferView.Format            = DXGI_FORMAT_R32_UINT;
    g_indexBufferView.SizeInBytes       = static_cast<UINT>(indexBufferSize);
}

// 定数バッファ生成
{
    // 定数バッファリソース生成
    D3D12_HEAP_PROPERTIES constantProperties    = {};
    constantProperties.Type                     = D3D12_HEAP_TYPE_UPLOAD;
    constantProperties.CPUPageProperty          = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    constantProperties.MemoryPoolPreference     = D3D12_MEMORY_POOL_UNKNOWN;
    constantProperties.CreationNodeMask         = 1;
    constantProperties.VisibleNodeMask          = 1;

    D3D12_RESOURCE_DESC constantResource        = {};
    constantResource.Dimension                  = D3D12_RESOURCE_DIMENSION_BUFFER;
    constantResource.Alignment                  = 0;
    constantResource.Width                      = 256;
    constantResource.Height                     = 1;
    constantResource.DepthOrArraySize           = 1;
    constantResource.MipLevels                  = 1;
    constantResource.Format                     = DXGI_FORMAT_UNKNOWN;
    constantResource.SampleDesc                 = { 1, 0 };
    constantResource.Layout                     = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    constantResource.Flags                      = D3D12_RESOURCE_FLAG_NONE;

    if( FAILED( g_device->CreateCommittedResource( &constantProperties, D3D12_HEAP_FLAG_NONE, &constantResource, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&g_constantBuffer) ) ) )
    {
        return false;
    }
}

// テクスチャ生成
{
    if( FAILED( CoInitializeEx( NULL, COINIT_MULTITHREADED ) ) )
    {
        return false;
    }

    // 読み込み
    std::unique_ptr<uint8_t[]>    wicData;
    D3D12_SUBRESOURCE_DATA      subresourceData;
    if( FAILED( LoadWICTextureFromFile( g_device.Get(), L"resource/image.png", &g_texture, wicData, subresourceData ) ) )
    {
        return false;
    }

    D3D12_RESOURCE_DESC textureResourceDesc = g_texture->GetDesc();
    UINT64 uploadBufferSize                 = GetRequiredIntermediateSize( g_texture.Get(), 0, 1 );

    // GPUアップロードバッファ生成
    D3D12_HEAP_PROPERTIES tmpHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
    D3D12_RESOURCE_DESC   tmpResourceDesc   = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
    if( FAILED( g_device->CreateCommittedResource(
        &tmpHeapProperties,
        D3D12_HEAP_FLAG_NONE,
        &tmpResourceDesc,
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(g_textureUploadHeap.GetAddressOf() ) ) ) )
    {
        return false;
    }

    // テクスチャ情報更新
    UpdateSubresources( g_commandList.Get(), g_texture.Get(), g_textureUploadHeap.Get(), 0, 0, 1, &subresourceData );
    D3D12_RESOURCE_BARRIER resourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition( g_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE );
    g_commandList->ResourceBarrier( 1, &resourceBarrier );

    // テクスチャデスクリプタヒープ生成
    D3D12_DESCRIPTOR_HEAP_DESC textureHeapDesc  = {};
    textureHeapDesc.NumDescriptors              = 1;
    textureHeapDesc.Type                        = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
    textureHeapDesc.Flags                       = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
    textureHeapDesc.NodeMask                    = 0;

    if( FAILED( g_device->CreateDescriptorHeap( &textureHeapDesc, IID_PPV_ARGS(&g_textureHeap) ) ) )
    {
        return false;
    }

    // シェーダーリソースビュー生成
    D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc  = {};
    shaderResourceViewDesc.Shader4ComponentMapping          = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
    shaderResourceViewDesc.Format                           = textureResourceDesc.Format;
    shaderResourceViewDesc.ViewDimension                    = D3D12_SRV_DIMENSION_TEXTURE2D;
    shaderResourceViewDesc.Texture2D.MipLevels              = 1;

    g_device->CreateShaderResourceView( g_texture.Get(), &shaderResourceViewDesc, g_textureHeap->GetCPUDescriptorHandleForHeapStart() );

    // アンロード
    g_commandList->DiscardResource( g_textureUploadHeap.Get(), nullptr );

    CoUninitialize();
}

// コマンドリストを閉じて実行
{
    if( FAILED( g_commandList->Close() ) )
    {
        return false;
    }

    ID3D12CommandList* ppCommandListArray[] = {g_commandList.Get()};
    g_commandQueue->ExecuteCommandLists( _countof(ppCommandListArray), ppCommandListArray );
}

描画処理の変更

  1. 定数バッファとテクスチャをシェーダレジスタに設定
  2. 生成したインデックスバッファビューを設定
  3. DrawIndexedInstancedを使用するように変更

テクスチャがY軸回転するようにしています。

// 描画
{
    // クリア
    float clearColor[] = { 0.f, 0.2f, 0.4f, 1.f };
    g_commandList->ClearRenderTargetView(handle, clearColor, 0, nullptr);

    ++ g_rotateCount;
    g_rotateCount %= 360;

    // 変換行列を定数バッファに書き込み
    XMMATRIX view       = XMMatrixLookAtLH( {0.f, 0.f, -5.f}, {0.f, 0.f, 0.f}, {0.f, 1.f, 0.f} );
    XMMATRIX projection = XMMatrixPerspectiveFovLH( XMConvertToRadians(60.f), g_acpectRatio, 1.f, 20.f );
    XMMATRIX rotate     = XMMatrixRotationY( XMConvertToRadians(static_cast<float>(g_rotateCount)) );

    XMFLOAT4X4 mat;
    XMStoreFloat4x4( &mat, XMMatrixTranspose(rotate * view * projection) );
    XMFLOAT4X4 * pBuffer = nullptr;
    g_constantBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&pBuffer) );
    *pBuffer = mat;
    g_constantBuffer->Unmap( 0, nullptr );
    // 定数バッファをシェーダレジスタに設定
    g_commandList->SetGraphicsRootConstantBufferView( 0, g_constantBuffer->GetGPUVirtualAddress() );

    // テクスチャをシェーダレジスタに設定
    ID3D12DescriptorHeap* pTextureHeaps[] = {g_textureHeap.Get()};
    g_commandList->SetDescriptorHeaps( _countof(pTextureHeaps), pTextureHeaps );
    g_commandList->SetGraphicsRootDescriptorTable( 1, g_textureHeap->GetGPUDescriptorHandleForHeapStart() );

    g_commandList->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
    g_commandList->IASetVertexBuffers( 0, 1, &g_vertexBufferView );
    g_commandList->IASetIndexBuffer( &g_indexBufferView );
    g_commandList->DrawIndexedInstanced( 6, 1, 0, 0, 0 );
}

シェーダーの変更

ルートシグネチャ生成時に設定した
定数バッファ    が register(b0)
テクスチャサンプラ が register(s0)
に格納されているのでシェーダープログラム側でそれぞれ参照します。

cbuffer cbTansMatrix : register(b0)
{
    float4x4 WVP;
}

Texture2D<float4> tex0 : register(t0);
SamplerState samp0 : register(s0);

struct VSInput
{
    float3 position : POSITION;
    float3 normal   : NORMAL;
    float2 uv       : TEXCOORD;
};

struct PSInput
{
    float4 position : SV_POSITION;
    float4 normal   : NORMAL;
    float2 uv       : TEXCOORD;
};

PSInput VSMain( VSInput input )
{
    PSInput output;

    float4 position = float4(input.position, 1.0f);
    float3 normal   = float4(input.normal, 1.0f);

    output.position = mul(position, WVP);
    output.normal   = mul(normal, WVP);
    output.uv       = input.uv;

    return output;
}

float4 PSMain( PSInput input ) : SV_TARGET
{
    return tex0.Sample(samp0, input.uv);
}

改めて見ると、今回が本当のスケルトンプロジェクトかもしれないです笑
(前回いろいろとおかしな箇所があり今回で直しました…)
次回はライト関係をしようかなと思います。
最後にmain.cpp全文を張り付けて終わりにしたいと思います。それではまた次回!

#include <windows.h>
#include <tchar.h>
#include <wrl.h>

#include <d3d12.h>
#pragma comment(lib, "d3d12.lib")
#include <dxgi1_4.h>
#pragma comment(lib, "dxgi.lib")
#include <D3Dcompiler.h>
#pragma comment(lib, "d3dcompiler.lib")

#include <DirectXMath.h>

#include "d3dx12.h"
#include "WICTextureLoader12.h"

using namespace DirectX;
using namespace Microsoft::WRL;

//-----------------------------------------------------------------
// 定数定義
//-----------------------------------------------------------------
#define WINDOW_CLASS        _T("Skeleton")
#define WINDOW_TITLE        WINDOW_CLASS
#define WINDOW_STYLE        WS_OVERLAPPEDWINDOW
#define WINDOW_WIDTH        1280
#define WINDOW_HEIGHT       720

#define FRAME_BUFFER_NUM    2

//! 頂点構造体
struct Vertex
{
    XMFLOAT3    _position;
    XMFLOAT3    _normal;
    XMFLOAT2    _uv;
};

//-----------------------------------------------------------------
// 関数定義
//-----------------------------------------------------------------
//! ウィンドウプロシージャ
LRESULT CALLBACK WindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
//! 初期化
bool Initialize( HWND hWnd );
//! 更新
bool Update( void );
//! 待ち
bool WaitFrame( void );

//-----------------------------------------------------------------
// 変数定義
//-----------------------------------------------------------------
D3D12_VIEWPORT                      g_viewPort                              = {0.f, 0.f, static_cast<float>(WINDOW_WIDTH), static_cast<float>(WINDOW_HEIGHT), 0.f, 1.f};
D3D12_RECT                          g_rect                                  = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
ComPtr<IDXGISwapChain3>               g_swapChain                             = nullptr;
ComPtr<ID3D12Device>              g_device                                = nullptr;
ComPtr<ID3D12Resource>                g_renderTargetArray[FRAME_BUFFER_NUM];
ComPtr<ID3D12CommandAllocator>        g_commandAllocator                      = nullptr;
ComPtr<ID3D12CommandQueue>            g_commandQueue                          = nullptr;
ComPtr<ID3D12RootSignature>           g_rootSignature                         = nullptr;
ComPtr<ID3D12DescriptorHeap>      g_heap                                  = nullptr;
ComPtr<ID3D12PipelineState>           g_pipelineState                         = nullptr;
ComPtr<ID3D12GraphicsCommandList> g_commandList                           = nullptr;
UINT                                g_heapSize                              = 0;

ComPtr<ID3D12Resource>                g_vertexBuffer                          = nullptr;
D3D12_VERTEX_BUFFER_VIEW            g_vertexBufferView;

ComPtr<ID3D12Resource>                g_indexBuffer                           = nullptr;
D3D12_INDEX_BUFFER_VIEW             g_indexBufferView;

ComPtr<ID3D12Resource>                g_constantBuffer                        = nullptr;

ComPtr<ID3D12Resource>                g_texture                               = nullptr;
ComPtr<ID3D12Resource>                g_textureUploadHeap                     = nullptr;
ComPtr<ID3D12DescriptorHeap>      g_textureHeap                           = nullptr;

// 同期オブジェクト
UINT                                g_frameIndex                            = 0;
HANDLE                              g_fenceHandle;
ComPtr<ID3D12Fence>                   g_fence                                 = nullptr;
UINT64                              g_fenceValue;

float                               g_acpectRatio                           = static_cast<float>(WINDOW_WIDTH) / static_cast<float>(WINDOW_HEIGHT);

bool                                g_useWarpDevice                         = false;

int                                 g_rotateCount                           = 0;

//-----------------------------------------------------------------
// 関数
//-----------------------------------------------------------------
//-----------------------------------------------------------------
// ウィンドウメイン
//-----------------------------------------------------------------
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, TCHAR* lpszCmdLine, int nCmdShow )
{
    // ウィンドウクラス生成
    WNDCLASSEX windowClass      = {};
    windowClass.cbSize          = sizeof(WNDCLASSEX);
    windowClass.style           = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
    windowClass.lpfnWndProc     = WindowProc;
    windowClass.hInstance       = hInstance;
    windowClass.hCursor         = LoadCursor(NULL, IDC_ARROW);
    windowClass.lpszClassName   = WINDOW_CLASS;
    RegisterClassEx( &windowClass );
    AdjustWindowRect( &g_rect, WINDOW_STYLE, false );

    // ウィンドウ生成
    HWND hWnd = CreateWindow(
        WINDOW_CLASS,
        WINDOW_TITLE,
        WINDOW_STYLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        g_rect.right - g_rect.left,
        g_rect.bottom - g_rect.top,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    // DirectX初期化
    if( !Initialize( hWnd ) )
    {
        MessageBox( hWnd, _T("DirectXの初期化に失敗しました"), _T("Initialize"), MB_OK | MB_ICONEXCLAMATION );
        return 0;
    }

    ShowWindow( hWnd, SW_SHOW );
    UpdateWindow( hWnd );

    MSG msg;
    while( true )
    {
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            if( msg.message == WM_QUIT )
            {
                break;
            }

            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
    }

    WaitFrame();
    CloseHandle( g_fenceHandle );

    return static_cast<int>( msg.wParam );
}

//-----------------------------------------------------------------
// ウィンドウプロシージャ
//-----------------------------------------------------------------
LRESULT CALLBACK WindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_PAINT:
        Update();
        break;
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;
    default:
        return DefWindowProc( hWnd, msg, wParam, lParam );
    }

    return 0;
}

//-----------------------------------------------------------------
// 初期化
//-----------------------------------------------------------------
bool Initialize( HWND hWnd )
{
    // DirectX12のデバッグレイヤー有効化
    {
        ComPtr<ID3D12Debug> debugController = nullptr;
        if( SUCCEEDED( D3D12GetDebugInterface( IID_PPV_ARGS(&debugController) ) ) )
        {
            debugController->EnableDebugLayer();
        }
    }

    {
        // ハードウェアアダプタ取得
        ComPtr<IDXGIFactory4> factory = nullptr;
        if( FAILED( CreateDXGIFactory1( IID_PPV_ARGS(&factory) ) ) )
        {
            return false;
        }

        if( g_useWarpDevice )
        {
            ComPtr<IDXGIAdapter> warpAdapter = nullptr;
            if( FAILED( factory->EnumWarpAdapter( IID_PPV_ARGS(&warpAdapter) ) ) )
            {
                return false;
            }
            if( FAILED( D3D12CreateDevice( warpAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_device) ) ) )
            {
                return false;
            }
        }
        else
        {
            ComPtr<IDXGIAdapter1> hardwareAdapter = nullptr;
            ComPtr<IDXGIAdapter1> adapter         = nullptr;
        
            for( UINT i = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters1(i, &adapter); i ++ )
            {
                DXGI_ADAPTER_DESC1 desc;
                adapter->GetDesc1( &desc );
                if( desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE )
                {
                    continue;
                }
                if( SUCCEEDED( D3D12CreateDevice( adapter.Get(), D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr ) ) )
                {
                    break;
                }
            }

            hardwareAdapter = adapter.Detach();

            if( FAILED( D3D12CreateDevice( hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_device) ) ) )
            {
                return false;
            }
        }
        // コマンドキュー生成
        D3D12_COMMAND_QUEUE_DESC queueDesc = {};
        queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
        queueDesc.Type  = D3D12_COMMAND_LIST_TYPE_DIRECT;

        if( FAILED( g_device->CreateCommandQueue( &queueDesc, IID_PPV_ARGS(&g_commandQueue) ) ) )
        {
            return false;
        }
        
        // スワップチェイン生成
        DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
        swapChainDesc.BufferCount           = FRAME_BUFFER_NUM;
        swapChainDesc.Width                 = WINDOW_WIDTH;
        swapChainDesc.Height                = WINDOW_HEIGHT;
        swapChainDesc.Format                = DXGI_FORMAT_R8G8B8A8_UNORM;
        swapChainDesc.BufferUsage           = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapChainDesc.SwapEffect            = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        swapChainDesc.SampleDesc.Count      = 1;

        ComPtr<IDXGISwapChain1> swapChain = nullptr;
        if( FAILED( factory->CreateSwapChainForHwnd( g_commandQueue.Get(), hWnd, &swapChainDesc, nullptr, nullptr, &swapChain ) ) )
        {
            return false;
        }
        if( FAILED( factory->MakeWindowAssociation( hWnd, DXGI_MWA_NO_ALT_ENTER ) ) )
        {
            return false;
        }
        if( FAILED( swapChain.As(&g_swapChain) ) )
        {
            return false;
        }

        g_frameIndex = g_swapChain->GetCurrentBackBufferIndex();
    }

    // ヒープ生成
    {
        D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
        heapDesc.NumDescriptors             = FRAME_BUFFER_NUM;
        heapDesc.Type                       = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
        heapDesc.Flags                      = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;

        if( FAILED( g_device->CreateDescriptorHeap( &heapDesc, IID_PPV_ARGS(&g_heap) ) ) )
        {
            return false;
        }

        g_heapSize = g_device->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE_RTV );
    }

    // レンダーターゲットビュー生成
    {
        D3D12_CPU_DESCRIPTOR_HANDLE handle = {};
        handle.ptr = g_heap->GetCPUDescriptorHandleForHeapStart().ptr;

        for( UINT i = 0; i < FRAME_BUFFER_NUM; i ++ )
        {
            if( FAILED( g_swapChain->GetBuffer( i, IID_PPV_ARGS(&g_renderTargetArray[i]) ) ) )
            {
                return false;
            }
            g_device->CreateRenderTargetView( g_renderTargetArray[i].Get(), nullptr, handle );
            handle.ptr += g_heapSize;
        }
    }

    // コマンドアロケータ生成
    if( FAILED( g_device->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_commandAllocator) ) ) ) 
    {
        return false;
    }

    // ルートシグネチャ生成
    {
        D3D12_DESCRIPTOR_RANGE      rangeArray[1];
        D3D12_ROOT_PARAMETER        rootParameterArray[2];
        D3D12_STATIC_SAMPLER_DESC   samplerDesc{};

        // 定数バッファをデスクリプタを介さずに渡すように設定
        rootParameterArray[0].ParameterType                         = D3D12_ROOT_PARAMETER_TYPE_CBV;
        rootParameterArray[0].Descriptor.ShaderRegister             = 0;
        rootParameterArray[0].Descriptor.RegisterSpace              = 0;
        rootParameterArray[0].ShaderVisibility                      = D3D12_SHADER_VISIBILITY_ALL;

        // テクスチャのデスクリプタヒープを渡すように設定
        rangeArray[0].RangeType                                     = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
        rangeArray[0].NumDescriptors                                = 1;
        rangeArray[0].BaseShaderRegister                            = 0;
        rangeArray[0].RegisterSpace                                 = 0;
        rangeArray[0].OffsetInDescriptorsFromTableStart             = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;

        rootParameterArray[1].ParameterType                         = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        rootParameterArray[1].ShaderVisibility                      = D3D12_SHADER_VISIBILITY_ALL;
        rootParameterArray[1].DescriptorTable.NumDescriptorRanges   = 1;
        rootParameterArray[1].DescriptorTable.pDescriptorRanges     = &rangeArray[0];

        // サンプラ設定
        samplerDesc.Filter                  = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
        samplerDesc.AddressU                = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        samplerDesc.AddressV                = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        samplerDesc.AddressW                = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
        samplerDesc.MipLODBias              = 0.f;
        samplerDesc.MaxAnisotropy           = 16;
        samplerDesc.ComparisonFunc          = D3D12_COMPARISON_FUNC_NEVER;
        samplerDesc.BorderColor             = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
        samplerDesc.MinLOD                  = 0.f;
        samplerDesc.MaxLOD                  = D3D12_FLOAT32_MAX;
        samplerDesc.ShaderRegister          = 0;
        samplerDesc.RegisterSpace           = 0;
        samplerDesc.ShaderVisibility        = D3D12_SHADER_VISIBILITY_ALL;

        // ルートシグネチャ生成
        D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
        rootSignatureDesc.NumParameters             = _countof(rootParameterArray);
        rootSignatureDesc.pParameters               = rootParameterArray;
        rootSignatureDesc.NumStaticSamplers         = 1;
        rootSignatureDesc.pStaticSamplers           = &samplerDesc;
        rootSignatureDesc.Flags                     = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;

        ComPtr<ID3DBlob> signature    = nullptr;
        ComPtr<ID3DBlob> error        = nullptr;

        if( FAILED( D3D12SerializeRootSignature( &rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error ) ) )
        {
            return false;
        }
        if( FAILED( g_device->CreateRootSignature( 0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&g_rootSignature) ) ) )
        {
            return false;
        }
    }

    // パイプラインステートオブジェクト生成
    {
        // シェーダーコンパイル
        ComPtr<ID3DBlob>  vertexShader = nullptr;
        ComPtr<ID3DBlob>  pixelShader  = nullptr;
        UINT                compileFlags = 0;

        if( FAILED( D3DCompileFromFile( L"resource/sample.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr ) ) )
        {
            return false;
        }
        if( FAILED( D3DCompileFromFile( L"resource/sample.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr ) ) )
        {
            return false;
        }

        // 頂点入力レイアウト定義
        D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = {
            {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
            {"NORMAL"  , 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
            {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT   , 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        };

        // グラフィックスパイプラインステート生成
        D3D12_GRAPHICS_PIPELINE_STATE_DESC gpsDesc  = {};
        gpsDesc.InputLayout                         = { inputElementDescs, _countof(inputElementDescs) };
        gpsDesc.pRootSignature                      = g_rootSignature.Get();
        {
            // 頂点シェーダー
            D3D12_SHADER_BYTECODE shaderBytecode;
            shaderBytecode.pShaderBytecode  = vertexShader->GetBufferPointer();
            shaderBytecode.BytecodeLength   = vertexShader->GetBufferSize();
            gpsDesc.VS = shaderBytecode;
        }
        {
            // ピクセルシェーダー
            D3D12_SHADER_BYTECODE shaderBytecode;
            shaderBytecode.pShaderBytecode  = pixelShader->GetBufferPointer();
            shaderBytecode.BytecodeLength   = pixelShader->GetBufferSize();
            gpsDesc.PS = shaderBytecode;
        }
        {
            // ラスタライザ
            D3D12_RASTERIZER_DESC rasterizerDesc    = {};
            rasterizerDesc.FillMode                 = D3D12_FILL_MODE_SOLID;
            rasterizerDesc.CullMode                 = D3D12_CULL_MODE_BACK;
            rasterizerDesc.FrontCounterClockwise    = false;
            rasterizerDesc.DepthBias                = D3D12_DEFAULT_DEPTH_BIAS;
            rasterizerDesc.DepthBiasClamp           = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
            rasterizerDesc.SlopeScaledDepthBias     = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
            rasterizerDesc.DepthClipEnable          = true;
            rasterizerDesc.MultisampleEnable        = false;
            rasterizerDesc.AntialiasedLineEnable    = false;
            rasterizerDesc.ForcedSampleCount        = 0;
            rasterizerDesc.ConservativeRaster       = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
            gpsDesc.RasterizerState = rasterizerDesc;
        }
        {
            // ブレンド
            D3D12_BLEND_DESC blendDesc          = {};
            blendDesc.AlphaToCoverageEnable     = false;
            blendDesc.IndependentBlendEnable    = false;
            for( UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i ++ )
            {
                blendDesc.RenderTarget[i].BlendEnable           = true;
                blendDesc.RenderTarget[i].LogicOpEnable         = false;
                blendDesc.RenderTarget[i].SrcBlend              = D3D12_BLEND_SRC_ALPHA;
                blendDesc.RenderTarget[i].DestBlend             = D3D12_BLEND_INV_SRC_ALPHA;
                blendDesc.RenderTarget[i].BlendOp               = D3D12_BLEND_OP_ADD;
                blendDesc.RenderTarget[i].SrcBlendAlpha         = D3D12_BLEND_ONE;
                blendDesc.RenderTarget[i].DestBlendAlpha        = D3D12_BLEND_ZERO;
                blendDesc.RenderTarget[i].BlendOpAlpha          = D3D12_BLEND_OP_ADD;
                blendDesc.RenderTarget[i].LogicOp               = D3D12_LOGIC_OP_NOOP;
                blendDesc.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
            }
            gpsDesc.BlendState = blendDesc;
        }
        gpsDesc.DepthStencilState.DepthEnable   = false;
        gpsDesc.DepthStencilState.StencilEnable = false;

        // レンダーターゲット
        gpsDesc.NumRenderTargets        = 1;
        gpsDesc.RTVFormats[0]           = DXGI_FORMAT_R8G8B8A8_UNORM;

        gpsDesc.PrimitiveTopologyType   = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;

        // サンプラ
        gpsDesc.SampleMask              = UINT_MAX;
        gpsDesc.SampleDesc.Count        = 1;
        gpsDesc.SampleDesc.Quality      = 0;

        gpsDesc.DSVFormat               = DXGI_FORMAT_D32_FLOAT;

        if( FAILED( g_device->CreateGraphicsPipelineState( &gpsDesc, IID_PPV_ARGS(&g_pipelineState) ) ) )
        {
            return false;
        }
    }

    // コマンドリスト生成
    {
        if( FAILED( g_device->CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_commandAllocator.Get(), g_pipelineState.Get(), IID_PPV_ARGS(&g_commandList) ) ) )
        {
            return false;
        }
    }

    // 頂点バッファ生成
    {
        // ジオメトリ定義
        Vertex vertexArray[] = {
            {{ -1.f,  1.f, 0.f}, {0.f, 0.f, -1.f}, {0.f, 0.f}},
            {{  1.f,  1.f, 0.f}, {0.f, 0.f, -1.f}, {1.f, 0.f}},
            {{  1.f, -1.f, 0.f}, {0.f, 0.f, -1.f}, {1.f, 1.f}},
            {{ -1.f, -1.f, 0.f}, {0.f, 0.f, -1.f}, {0.f, 1.f}}
        };

        UINT vertexBufferSize = sizeof(vertexArray);

        D3D12_HEAP_PROPERTIES heapProperties    = {};
        heapProperties.Type                     = D3D12_HEAP_TYPE_UPLOAD;
        heapProperties.CPUPageProperty          = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
        heapProperties.MemoryPoolPreference     = D3D12_MEMORY_POOL_UNKNOWN;
        heapProperties.CreationNodeMask         = 1;
        heapProperties.VisibleNodeMask          = 1;

        D3D12_RESOURCE_DESC resourceDesc        = {};
        resourceDesc.Dimension                  = D3D12_RESOURCE_DIMENSION_BUFFER;
        resourceDesc.Alignment                  = 0;
        resourceDesc.Width                      = vertexBufferSize;
        resourceDesc.Height                     = 1;
        resourceDesc.DepthOrArraySize           = 1;
        resourceDesc.MipLevels                  = 1;
        resourceDesc.Format                     = DXGI_FORMAT_UNKNOWN;
        resourceDesc.SampleDesc.Count           = 1;
        resourceDesc.SampleDesc.Quality         = 0;
        resourceDesc.Layout                     = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
        resourceDesc.Flags                      = D3D12_RESOURCE_FLAG_NONE;

        if( FAILED( g_device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&g_vertexBuffer) ) ) )
        {
            return false;
        }
        
        // データ設定
        UINT8* pVertexDataBegin;
        D3D12_RANGE readRange = {0, 0};
        if( FAILED( g_vertexBuffer->Map( 0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin) ) ) )
        {
            return false;
        }
        memcpy( pVertexDataBegin, vertexArray, vertexBufferSize );
        g_vertexBuffer->Unmap( 0, nullptr );

        // ビュー初期化
        g_vertexBufferView.BufferLocation   = g_vertexBuffer->GetGPUVirtualAddress();
        g_vertexBufferView.StrideInBytes    = sizeof(Vertex);
        g_vertexBufferView.SizeInBytes      = vertexBufferSize;
    }

    // インデックスバッファ生成
    {
        uint32_t indices[] = { 0, 1, 2, 0, 2, 3 }; // 描画順

        auto indexBufferSize = sizeof(indices);

        D3D12_HEAP_PROPERTIES heapProperties    = {};
        heapProperties.Type                     = D3D12_HEAP_TYPE_UPLOAD;
        heapProperties.CPUPageProperty          = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
        heapProperties.MemoryPoolPreference     = D3D12_MEMORY_POOL_UNKNOWN;
        heapProperties.CreationNodeMask         = 1;
        heapProperties.VisibleNodeMask          = 1;

        D3D12_RESOURCE_DESC resourceDesc        = {};
        resourceDesc.Dimension                  = D3D12_RESOURCE_DIMENSION_BUFFER;
        resourceDesc.Alignment                  = 0;
        resourceDesc.Width                      = indexBufferSize;
        resourceDesc.Height                     = 1;
        resourceDesc.DepthOrArraySize           = 1;
        resourceDesc.MipLevels                  = 1;
        resourceDesc.Format                     = DXGI_FORMAT_UNKNOWN;
        resourceDesc.SampleDesc.Count           = 1;
        resourceDesc.SampleDesc.Quality         = 0;
        resourceDesc.Layout                     = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
        resourceDesc.Flags                      = D3D12_RESOURCE_FLAG_NONE;

        if( FAILED( g_device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&g_indexBuffer) ) ) )
        {
            return false;
        }

        // データ設定
        UINT8* pIndexDataBegin;
        if ( FAILED( g_indexBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&pIndexDataBegin) ) ) )
        {
            return false;
        }
        memcpy( pIndexDataBegin, indices, indexBufferSize );
        g_indexBuffer->Unmap( 0, nullptr );

        // ビュー初期化
        g_indexBufferView.BufferLocation    = g_indexBuffer->GetGPUVirtualAddress();
        g_indexBufferView.Format            = DXGI_FORMAT_R32_UINT;
        g_indexBufferView.SizeInBytes       = static_cast<UINT>(indexBufferSize);
    }

    // 定数バッファ生成
    {
        // 定数バッファリソース生成
        D3D12_HEAP_PROPERTIES constantProperties    = {};
        constantProperties.Type                     = D3D12_HEAP_TYPE_UPLOAD;
        constantProperties.CPUPageProperty          = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
        constantProperties.MemoryPoolPreference     = D3D12_MEMORY_POOL_UNKNOWN;
        constantProperties.CreationNodeMask         = 1;
        constantProperties.VisibleNodeMask          = 1;

        D3D12_RESOURCE_DESC constantResource        = {};
        constantResource.Dimension                  = D3D12_RESOURCE_DIMENSION_BUFFER;
        constantResource.Alignment                  = 0;
        constantResource.Width                      = 256;
        constantResource.Height                     = 1;
        constantResource.DepthOrArraySize           = 1;
        constantResource.MipLevels                  = 1;
        constantResource.Format                     = DXGI_FORMAT_UNKNOWN;
        constantResource.SampleDesc                 = { 1, 0 };
        constantResource.Layout                     = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
        constantResource.Flags                      = D3D12_RESOURCE_FLAG_NONE;

        if( FAILED( g_device->CreateCommittedResource( &constantProperties, D3D12_HEAP_FLAG_NONE, &constantResource, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&g_constantBuffer) ) ) )
        {
            return false;
        }
    }

    // テクスチャ生成
    {
        if( FAILED( CoInitializeEx( NULL, COINIT_MULTITHREADED ) ) )
        {
            return false;
        }

        // 読み込み
        std::unique_ptr<uint8_t[]>    wicData;
        D3D12_SUBRESOURCE_DATA      subresourceData;
        if( FAILED( LoadWICTextureFromFile( g_device.Get(), L"resource/image.png", &g_texture, wicData, subresourceData ) ) )
        {
            return false;
        }

        D3D12_RESOURCE_DESC textureResourceDesc = g_texture->GetDesc();
        UINT64 uploadBufferSize                 = GetRequiredIntermediateSize( g_texture.Get(), 0, 1 );

        // GPUアップロードバッファ生成
        D3D12_HEAP_PROPERTIES tmpHeapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
        D3D12_RESOURCE_DESC   tmpResourceDesc   = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
        if( FAILED( g_device->CreateCommittedResource(
            &tmpHeapProperties,
            D3D12_HEAP_FLAG_NONE,
            &tmpResourceDesc,
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(g_textureUploadHeap.GetAddressOf() ) ) ) )
        {
            return false;
        }

        // テクスチャ情報更新
        UpdateSubresources( g_commandList.Get(), g_texture.Get(), g_textureUploadHeap.Get(), 0, 0, 1, &subresourceData );
        D3D12_RESOURCE_BARRIER resourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition( g_texture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE );
        g_commandList->ResourceBarrier( 1, &resourceBarrier );

        // テクスチャデスクリプタヒープ生成
        D3D12_DESCRIPTOR_HEAP_DESC textureHeapDesc  = {};
        textureHeapDesc.NumDescriptors              = 1;
        textureHeapDesc.Type                        = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        textureHeapDesc.Flags                       = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
        textureHeapDesc.NodeMask                    = 0;

        if( FAILED( g_device->CreateDescriptorHeap( &textureHeapDesc, IID_PPV_ARGS(&g_textureHeap) ) ) )
        {
            return false;
        }

        // シェーダーリソースビュー生成
        D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc  = {};
        shaderResourceViewDesc.Shader4ComponentMapping          = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
        shaderResourceViewDesc.Format                           = textureResourceDesc.Format;
        shaderResourceViewDesc.ViewDimension                    = D3D12_SRV_DIMENSION_TEXTURE2D;
        shaderResourceViewDesc.Texture2D.MipLevels              = 1;

        g_device->CreateShaderResourceView( g_texture.Get(), &shaderResourceViewDesc, g_textureHeap->GetCPUDescriptorHandleForHeapStart() );

        // アンロード
        g_commandList->DiscardResource( g_textureUploadHeap.Get(), nullptr );

        CoUninitialize();
    }

    // コマンドリストを閉じて実行
    {
        if( FAILED( g_commandList->Close() ) )
        {
            return false;
        }

        ID3D12CommandList* ppCommandListArray[] = {g_commandList.Get()};
        g_commandQueue->ExecuteCommandLists( _countof(ppCommandListArray), ppCommandListArray );
    }

    // 同期オブジェクトを生成してリソースがGPUに転送されるまで待機する
    {
        if( FAILED( g_device->CreateFence( 0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence) ) ) )
        {
            return false;
        }
        g_fenceValue = 1;

        g_fenceHandle = CreateEvent( nullptr, false, false , nullptr );
        if( !g_fenceHandle )
        {
            if( FAILED( HRESULT_FROM_WIN32(GetLastError()) ) )
            {
                return false;
            }
        }
        if( !WaitFrame() )
        {
            return false;
        }
    }

    return true;
}

//-----------------------------------------------------------------
// 更新
//-----------------------------------------------------------------
bool Update( void )
{
    if( FAILED( g_commandAllocator->Reset() ) )
    {
        return false;
    }
    if( FAILED( g_commandList->Reset( g_commandAllocator.Get(), g_pipelineState.Get() ) ) )
    {
        return false;
    }

    g_commandList->SetGraphicsRootSignature( g_rootSignature.Get() );

    g_commandList->RSSetViewports( 1, &g_viewPort );
    g_commandList->RSSetScissorRects( 1, &g_rect );

    // 表示準備
    {
        D3D12_RESOURCE_BARRIER resourceBarrier = {};
        resourceBarrier.Type                    = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
        resourceBarrier.Flags                   = D3D12_RESOURCE_BARRIER_FLAG_NONE;
        resourceBarrier.Transition.pResource    = g_renderTargetArray[g_frameIndex].Get();
        resourceBarrier.Transition.StateBefore  = D3D12_RESOURCE_STATE_PRESENT;
        resourceBarrier.Transition.StateAfter   = D3D12_RESOURCE_STATE_RENDER_TARGET;
        resourceBarrier.Transition.Subresource  = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
        g_commandList->ResourceBarrier( 1, &resourceBarrier );
    }

    D3D12_CPU_DESCRIPTOR_HANDLE handle = {};
    handle.ptr = g_heap->GetCPUDescriptorHandleForHeapStart().ptr + g_frameIndex * g_heapSize;
    g_commandList->OMSetRenderTargets( 1, &handle, false, nullptr );

    // 描画
    {
        // クリア
        float clearColor[] = { 0.f, 0.2f, 0.4f, 1.f };
        g_commandList->ClearRenderTargetView(handle, clearColor, 0, nullptr);

        ++ g_rotateCount;
        g_rotateCount %= 360;

        // 変換行列を定数バッファに書き込み
        XMMATRIX view       = XMMatrixLookAtLH( {0.f, 0.f, -5.f}, {0.f, 0.f, 0.f}, {0.f, 1.f, 0.f} );
        XMMATRIX projection = XMMatrixPerspectiveFovLH( XMConvertToRadians(60.f), g_acpectRatio, 1.f, 20.f );
        XMMATRIX rotate     = XMMatrixRotationY( XMConvertToRadians(static_cast<float>(g_rotateCount)) );

        XMFLOAT4X4 mat;
        XMStoreFloat4x4( &mat, XMMatrixTranspose(rotate * view * projection) );
        XMFLOAT4X4 * pBuffer = nullptr;
        g_constantBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&pBuffer) );
        *pBuffer = mat;
        g_constantBuffer->Unmap( 0, nullptr );
        // 定数バッファをシェーダレジスタに設定
        g_commandList->SetGraphicsRootConstantBufferView( 0, g_constantBuffer->GetGPUVirtualAddress() );

        // テクスチャをシェーダレジスタに設定
        ID3D12DescriptorHeap* pTextureHeaps[] = {g_textureHeap.Get()};
        g_commandList->SetDescriptorHeaps( _countof(pTextureHeaps), pTextureHeaps );
        g_commandList->SetGraphicsRootDescriptorTable( 1, g_textureHeap->GetGPUDescriptorHandleForHeapStart() );

        g_commandList->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
        g_commandList->IASetVertexBuffers( 0, 1, &g_vertexBufferView );
        g_commandList->IASetIndexBuffer( &g_indexBufferView );
        g_commandList->DrawIndexedInstanced( 6, 1, 0, 0, 0 );
    }

    // 表示
    {
        D3D12_RESOURCE_BARRIER resourceBarrier  = {};
        resourceBarrier.Type                    = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
        resourceBarrier.Flags                   = D3D12_RESOURCE_BARRIER_FLAG_NONE;
        resourceBarrier.Transition.pResource    = g_renderTargetArray[g_frameIndex].Get();
        resourceBarrier.Transition.StateBefore  = D3D12_RESOURCE_STATE_RENDER_TARGET;
        resourceBarrier.Transition.StateAfter   = D3D12_RESOURCE_STATE_PRESENT;
        resourceBarrier.Transition.Subresource  = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
        g_commandList->ResourceBarrier( 1, &resourceBarrier );
    }

    if( FAILED( g_commandList->Close() ) )
    {
        return false;
    }

    // コマンドリスト実行
    ID3D12CommandList* ppCommandListArray[] = {g_commandList.Get()};
    g_commandQueue->ExecuteCommandLists( _countof(ppCommandListArray), ppCommandListArray );

    // 出力
    if( FAILED( g_swapChain->Present( 1, 0 ) ) )
    {
        return false;
    }

    return WaitFrame();
}

//-----------------------------------------------------------------
// 待ち
//-----------------------------------------------------------------
bool WaitFrame( void )
{
    UINT64 fence = g_fenceValue;
    if( FAILED( g_commandQueue->Signal( g_fence.Get(), fence ) ) )
    {
        return false;
    }
    g_fenceValue ++;

    // 前のフレームが終了するまで待機
    if( g_fence->GetCompletedValue() < fence )
    {
        if( FAILED( g_fence->SetEventOnCompletion( fence, g_fenceHandle ) ) )
        {
            return false;
        }

        WaitForSingleObject( g_fenceHandle, INFINITE );
    }

    g_frameIndex = g_swapChain->GetCurrentBackBufferIndex();
    return true;
}