アヒルのある日

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

DX12との格闘 #1

こんにちは、ちゃらいプログラマです。
いろいろ話題のレイトレーシングをやってみたいなーと思ったのですが

  • DirectX9で知識が止まっている…
  • DirectX12を勉強しなきゃ!

となりました。
手始めにポリゴンを描画できるところまでやってみたのでご紹介します。
(軽く地獄をみました…)

概要

  • 画面中心にポリゴンを描画しています
  • 定数バッファを使用してシェーダー側にパラメータを渡して頂点カラーを変更しています
  • ※main.cppのみに処理を記述しています

インクルードと定義周り

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

#include <time.h>
#pragma comment(lib, "winmm.lib")
#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>

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;
    XMFLOAT4    _color;
};

//-----------------------------------------------------------------
// 関数定義
//-----------------------------------------------------------------
//! ウィンドウプロシージャ
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<ID3D12DescriptorHeap>      g_constantHeap                          = 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_constantBuffer                        = nullptr;
UINT8*                              g_constantBufferData                    = 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;

DWORD                               g_startTime;
  • DirectX9で時が止まっている自分としては、CommandAllocator?RootSignature?なんだなんだ…状態です。

エントリーポイントとウィンドウプロシージャ関数

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 );

    g_constantBuffer->Unmap( 0, nullptr );

    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_HEAP_DESC constantDesc     = {};
        constantDesc.NumDescriptors                 = 1;
        constantDesc.Type                           = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
        constantDesc.Flags                          = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;

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

        // 定数バッファリソース生成
        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;
        }

        // 定数バッファビュー生成
        D3D12_CONSTANT_BUFFER_VIEW_DESC constantView = {};
        constantView.BufferLocation                 = g_constantBuffer->GetGPUVirtualAddress();
        constantView.SizeInBytes                    = 256;
        g_device->CreateConstantBufferView( &constantView, g_constantHeap->GetCPUDescriptorHandleForHeapStart() );

        // マップ
        g_constantBuffer->Map( 0, nullptr, reinterpret_cast<void**>(&g_constantBufferData) );
    }

    {
        // 定数バッファをルートシグネチャパラメータへ設定
        D3D12_DESCRIPTOR_RANGE  rangeArray[1];
        D3D12_ROOT_PARAMETER    rootParameterArray[1];
        rangeArray[0].RangeType                                     = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
        rangeArray[0].NumDescriptors                                = 1;
        rangeArray[0].BaseShaderRegister                            = 0;
        rangeArray[0].RegisterSpace                                 = 0;
        rangeArray[0].OffsetInDescriptorsFromTableStart             = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
        rootParameterArray[0].ParameterType                         = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
        rootParameterArray[0].DescriptorTable.NumDescriptorRanges   = 1;
        rootParameterArray[0].DescriptorTable.pDescriptorRanges     = rangeArray;
        rootParameterArray[0].ShaderVisibility                      = D3D12_SHADER_VISIBILITY_ALL;

        // ルートシグネチャ生成
        D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
        rootSignatureDesc.NumParameters             = _countof(rootParameterArray);
        rootSignatureDesc.pParameters               = rootParameterArray;
        rootSignatureDesc.NumStaticSamplers         = 0;
        rootSignatureDesc.pStaticSamplers           = nullptr;
        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},
            {"COLOR"   , 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, 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           = false;
                blendDesc.RenderTarget[i].LogicOpEnable         = false;
                blendDesc.RenderTarget[i].SrcBlend              = D3D12_BLEND_ONE;
                blendDesc.RenderTarget[i].DestBlend             = D3D12_BLEND_ZERO;
                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.SampleMask                      = UINT_MAX;
        gpsDesc.PrimitiveTopologyType           = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
        gpsDesc.NumRenderTargets                = 1;
        gpsDesc.RTVFormats[0]                   = DXGI_FORMAT_R8G8B8A8_UNORM;
        gpsDesc.SampleDesc.Count                = 1;

        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;
        }
        if( FAILED( g_commandList->Close() ) )
        {
            return false;
        }
    }

    // 頂点バッファ生成
    {
        // ジオメトリ定義
        Vertex vertexArray[] = {
            {{ 0.f  ,  0.25f * g_acpectRatio, 0.f}, {1.f, 0.f, 0.f, 1.f}},
            {{ 0.25f, -0.25f * g_acpectRatio, 0.f}, {0.f, 1.f, 0.f, 1.f}},
            {{-0.25f, -0.25f * g_acpectRatio, 0.f}, {0.f, 0.f, 1.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;
    }

    // 同期オブジェクトを生成してリソースが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;
        }
    }

    g_startTime = timeGetTime();

    return true;
}

流れとしては

  1. デバイスを生成します。失敗する場合は、ビデオカードがDirectX12に対応していないことになります。
  2. コマンドキューを生成します。コマンドリストに描画情報を設定してコマンドキューに渡すことになります。
  3. スワップチェインを生成します。
  4. 「記述子ヒープ」を生成して、レンダーターゲットビューを生成します。フレームバッファとバックバッファを生成しています。
  5. ルートシグネチャを生成します。定数バッファとシェーダーの関連付けを行います。今回は1つのパラメータを関連付けしています。
  6. シェーダーをコンパイルします。合わせて、グラフィックスパイプラインステートオブジェクトを生成します。
  7. コマンドリストを生成します。
  8. 頂点バッファを生成します。同期オブジェクトを生成して、GPUにアップロードされるまで待機する必要があります。

更新関数

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

    // 定数バッファ更新
    {
        DWORD t = timeGetTime();
        float time = static_cast<float>((t - g_startTime) / 10.f) * (3.14f / 180.f);
        memcpy(g_constantBufferData, reinterpret_cast<void**>(&time), sizeof(time));
    }

    ID3D12DescriptorHeap* ppHeaps[] = { g_constantHeap.Get() };

    g_commandList->SetGraphicsRootSignature( g_rootSignature.Get() );
    g_commandList->SetDescriptorHeaps( _countof(ppHeaps), ppHeaps );
    g_commandList->SetGraphicsRootDescriptorTable( 0, g_constantHeap->GetGPUDescriptorHandleForHeapStart() );

    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_commandList->IASetPrimitiveTopology( D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
        g_commandList->IASetVertexBuffers( 0, 1, &g_vertexBufferView );
        g_commandList->DrawInstanced( 3, 1, 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();
}

流れとしては

  1. コマンドアロケータをリセットします。
  2. コマンドリストをリセットします。
  3. 定数バッファの値を更新します。
  4. コマンドリストにルートシグネチャを設定後に描画情報を設定します。
  5. コマンドキューを使用してコマンドリストを実行します。
  6. 同期待ちを行います。

同期待ち関数

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;
}

sample.hlsl

struct PSInput
{
    float4 position : SV_POSITION;
    float4 color    : COLOR;
};

float g_time : register(b0);

PSInput VSMain( float4 position : POSITION, float4 color : COLOR )
{
    PSInput input;

    input.position = position;
    input.color = color;

    return input;
}

float4 PSMain( PSInput input ) : SV_TARGET
{
    float4 color = input.color;
    color.r *= sin(g_time);
    color.g *= sin(g_time * 2);
    color.b *= sin(g_time * 4);
    return color;
}
  • g_time を定数バッファを使用して受け取れるようにしています。

と、長々書きましたが、とりあえず動いた!レベルなので目標にはまだまだ時間がかかりそうです笑
次はメッシュを描画してみようかなと…思ったり。

ではまた次回!