LoginSignup
11
11

More than 5 years have passed since last update.

C++を使ってDirectX12を初期化

Last updated at Posted at 2018-12-06

DirextX11を使用してましたがDX12もいいかなと思い、覚書

■Step1:環境構築

VisualStudio2017を使用しています。

■Step2:プロジェクト作成

VisualStudio2017を起動して「ファイル(F)」「新規作成(N)」「プロジェクト(p)」を選択します
2018-12-06 (2).png
今回はC++で構築するので「VisualC++」の「その他」から「空のプロジェクト」を選択します。
名前は適当に(今回はtest01としています)
2018-12-06 (5).png
「OK」を押しますと以下のようになります、ここで今後の為「X64」に変更しておきましょう
2018-12-06 (6).png

■Step3:test01.cppを作成

test01.cppを追加するために「ソースファイル」で右クリックするとサブメニューがでるので、そこで「追加(D)」「新しい項目(W)」を選択してください。
2018-12-06 (7).png
名前を「test01.cpp」にし「C++ファイル(.cpp)」が選択されていることを確認してから「追加(A)」を押します。
2018-12-06 (8).png
test01.cppが作成できたので
2018-12-06 (9).png

test01.cpp
#include    <Windows.h>
#include    <tchar.h>

#define WINDOW_WIDTH  720
#define WINDOW_HEIGHT 480

LRESULT CALLBACK    WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
#define CLASS_NAME  "CLASS TEST01"
#define PROC_NAME   "test01"

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPTSTR lpCmdLine, int nCmdShow)
{
    HWND            hwnd;
    MSG             msg;
    if (!hPreInst) {
        WNDCLASS    my_prog;
        my_prog.style           = CS_HREDRAW | CS_VREDRAW;
        my_prog.lpfnWndProc     = WndProc;
        my_prog.cbClsExtra      = 0;
        my_prog.cbWndExtra      = 0;
        my_prog.hInstance       = hInstance;
        my_prog.hIcon           = NULL;
        my_prog.hCursor         = LoadCursor(NULL, IDC_ARROW);
        my_prog.hbrBackground   = NULL;
        my_prog.lpszMenuName    = NULL;
        my_prog.lpszClassName   = _T(CLASS_NAME);

        if (!RegisterClass(&my_prog)) {
            return FALSE;
        }
    }

    RECT rect = {
        (LONG)0, 
        (LONG)0, 
        (LONG)(WINDOW_WIDTH), 
        (LONG)(WINDOW_HEIGHT)
    };
    AdjustWindowRect(
        &rect,                                    // クライアント矩形
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,  // ウィンドウスタイル
        FALSE                                     // メニューフラグ
    );
    hwnd = CreateWindow(_T(CLASS_NAME),
        _T(PROC_NAME),
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        rect.right - rect.left,           // ウィンドウの幅
        rect.bottom - rect.top,           // ウィンドウの高さ
        NULL,
        NULL,
        hInstance,
        NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    do {
        if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    } while (msg.message != WM_QUIT);
    return (int)(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return(DefWindowProc(hwnd, msg, wParam, lParam));
    }
    return (0L);
}

このようにします。

■Step4:仮実行

実行するとこんな感じ
2018-12-06 (10).png

■Step5:DirextX12を制御

DirectX12を制御するクラス「DX12Renderere.cpp」「DX12Renderere.h」を追加します。
2018-12-06 (11).png

DX12Renderer.cpp
#include <fstream>
#include "DX12Renderer.h"

DX12Renderer::DX12Renderer(HWND hwnd, int window_width, int window_height) :
    _window_hwnd(hwnd),
    _window_width(window_width),
    _window_height(window_height){

    UINT flagsDXGI = 0;
    ID3D12Debug* debug = nullptr;
    HRESULT hr;
#if _DEBUG
    D3D12GetDebugInterface(IID_PPV_ARGS(&debug));
    if (debug) {
        debug->EnableDebugLayer();
        debug->Release();
    }
    flagsDXGI |= DXGI_CREATE_FACTORY_DEBUG;
#endif
    hr = CreateDXGIFactory2(flagsDXGI, IID_PPV_ARGS(_factory.ReleaseAndGetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    ComPtr<IDXGIAdapter> adapter;
    hr = _factory->EnumAdapters(0, adapter.GetAddressOf());
    if (FAILED(hr)) {
        return;
    }
    // デバイス生成.
    // D3D12は 最低でも D3D_FEATURE_LEVEL_11_0 を要求するようだ.
    hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(_device.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    // コマンドアロケータを生成.
    hr = _device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(_command_allocator.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    // コマンドキューを生成.
    D3D12_COMMAND_QUEUE_DESC desc_command_queue;
    ZeroMemory(&desc_command_queue, sizeof(desc_command_queue));
    desc_command_queue.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    desc_command_queue.Priority = 0;
    desc_command_queue.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    hr = _device->CreateCommandQueue(&desc_command_queue, IID_PPV_ARGS(_command_queue.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    // コマンドキュー用のフェンスを準備.
    _fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
    hr = _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(_queue_fence.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }

    // スワップチェインを生成.
    DXGI_SWAP_CHAIN_DESC desc_swap_chain;
    ZeroMemory(&desc_swap_chain, sizeof(desc_swap_chain));
    desc_swap_chain.BufferCount = 2;
    desc_swap_chain.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc_swap_chain.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_swap_chain.OutputWindow = _window_hwnd;
    desc_swap_chain.SampleDesc.Count = 1;
    desc_swap_chain.Windowed = TRUE;
    desc_swap_chain.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    desc_swap_chain.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    hr = _factory->CreateSwapChain(_command_queue.Get(), &desc_swap_chain, (IDXGISwapChain**)_swap_chain.GetAddressOf());
    if (FAILED(hr)) {
        return;
    }
    // コマンドリストの作成.
    hr = _device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, _command_allocator.Get(), nullptr, IID_PPV_ARGS(_command_list.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }

    // ディスクリプタヒープ(RenderTarget用)の作成.
    D3D12_DESCRIPTOR_HEAP_DESC desc_heap;
    ZeroMemory(&desc_heap, sizeof(desc_heap));
    desc_heap.NumDescriptors = 2;
    desc_heap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    desc_heap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    hr = _device->CreateDescriptorHeap(&desc_heap, IID_PPV_ARGS(_descriptor_heap.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }

    // レンダーターゲット(プライマリ用)の作成.
    UINT strideHandleBytes = _device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    for (UINT i = 0;i < desc_swap_chain.BufferCount;++i) {
        hr = _swap_chain->GetBuffer(i, IID_PPV_ARGS(_render_target[i].GetAddressOf()));
        if (FAILED(hr)) {
            return;
        }
        _rtv_handle[i] = _descriptor_heap->GetCPUDescriptorHandleForHeapStart();
        _rtv_handle[i].ptr += i * strideHandleBytes;
        _device->CreateRenderTargetView(_render_target[i].Get(), nullptr, _rtv_handle[i]);
    }
    return;
}

DX12Renderer::~DX12Renderer() {}


void DX12Renderer::SetResourceBarrier(ID3D12GraphicsCommandList* commandList, ID3D12Resource* resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
{
    D3D12_RESOURCE_BARRIER descBarrier;
    ZeroMemory(&descBarrier, sizeof(descBarrier));
    descBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    descBarrier.Transition.pResource = resource;
    descBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    descBarrier.Transition.StateBefore = before;
    descBarrier.Transition.StateAfter = after;
    commandList->ResourceBarrier(1, &descBarrier);
}

void DX12Renderer::WaitForCommandQueue(ID3D12CommandQueue* pCommandQueue) {
    static UINT64 frames = 0;
    _queue_fence->SetEventOnCompletion(frames, _fence_event);
    pCommandQueue->Signal(_queue_fence.Get(), frames);
    WaitForSingleObject(_fence_event, INFINITE);
    frames++;
}

//  描画
HRESULT DX12Renderer::Render() {
    float clearColor[4] = { 1.0f,0.0f,0.0f,0.0f };
    D3D12_VIEWPORT viewport;
    viewport.TopLeftX   = 0; viewport.TopLeftY = 0;
    viewport.Width      = (FLOAT)_window_width;
    viewport.Height     = (FLOAT)_window_height;
    viewport.MinDepth   = 0;
    viewport.MaxDepth   = 1;

    int targetIndex = _swap_chain->GetCurrentBackBufferIndex();

    SetResourceBarrier(
        _command_list.Get(),
        _render_target[targetIndex].Get(),
        D3D12_RESOURCE_STATE_PRESENT,
        D3D12_RESOURCE_STATE_RENDER_TARGET);

    // レンダーターゲットのクリア処理.
    _command_list->RSSetViewports(1, &viewport);
    _command_list->ClearRenderTargetView(_rtv_handle[targetIndex], clearColor, 0, nullptr);

    // Presentする前の準備.
    SetResourceBarrier(
        _command_list.Get(),
        _render_target[targetIndex].Get(),
        D3D12_RESOURCE_STATE_RENDER_TARGET,
        D3D12_RESOURCE_STATE_PRESENT);

    _command_list->Close();

    // 積んだコマンドの実行.
    ID3D12CommandList* pCommandList = _command_list.Get();
    _command_queue->ExecuteCommandLists(1, &pCommandList);
    _swap_chain->Present(1, 0);

    WaitForCommandQueue(_command_queue.Get());
    _command_allocator->Reset();
    _command_list->Reset(_command_allocator.Get(), nullptr);
    return S_OK;
}
DX12Renderer.h
#pragma once

#include <Windows.h>
#include <d3d12.h>
#include <d3d12shader.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include <wrl/client.h>


using namespace DirectX;
using namespace Microsoft::WRL;

class DX12Renderer {
public:
    static constexpr int RTV_NUM = 2;

public:
    DX12Renderer(HWND hwnd, int window_width, int window_height);
    ~DX12Renderer();
    HRESULT Render();

private:
    HWND    _window_hwnd;
    int     _window_width;
    int     _window_height;

    HANDLE  _fence_event;

    ComPtr<ID3D12Device> _device;
    ComPtr<ID3D12CommandQueue> _command_queue;
    ComPtr<ID3D12CommandAllocator> _command_allocator;
    ComPtr<ID3D12GraphicsCommandList> _command_list;
    ComPtr<IDXGISwapChain3> _swap_chain;
    ComPtr<ID3D12Fence>  _queue_fence;
    ComPtr<IDXGIFactory3> _factory;
    ComPtr<ID3D12DescriptorHeap> _descriptor_heap;
    ComPtr<ID3D12Resource> _render_target[2];
    D3D12_CPU_DESCRIPTOR_HANDLE _rtv_handle[2];

    void SetResourceBarrier(ID3D12GraphicsCommandList* commandList, ID3D12Resource* resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after);
    void WaitForCommandQueue(ID3D12CommandQueue* pCommandQueue);
};

■Setp6:test01.cppの編集

test01.cppからDX12Rendererを呼び出すよう修正します。

test01.cpp
#include    <Windows.h>
#include    <tchar.h>
#include    "DX12Renderer.h"  //<追加>

#define WINDOW_WIDTH  720
#define WINDOW_HEIGHT 480

LRESULT CALLBACK    WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
#define CLASS_NAME  "CLASS TEST01"
#define PROC_NAME   "test01"

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPTSTR lpCmdLine, int nCmdShow)
{
    HWND            hwnd;
    MSG             msg;
    if (!hPreInst) {
        WNDCLASS    my_prog;
        my_prog.style           = CS_HREDRAW | CS_VREDRAW;
        my_prog.lpfnWndProc     = WndProc;
        my_prog.cbClsExtra      = 0;
        my_prog.cbWndExtra      = 0;
        my_prog.hInstance       = hInstance;
        my_prog.hIcon           = NULL;
        my_prog.hCursor         = LoadCursor(NULL, IDC_ARROW);
        my_prog.hbrBackground   = NULL;
        my_prog.lpszMenuName    = NULL;
        my_prog.lpszClassName   = _T(CLASS_NAME);

        if (!RegisterClass(&my_prog)) {
            return FALSE;
        }
    }

    RECT rect = {
        (LONG)0, 
        (LONG)0, 
        (LONG)(WINDOW_WIDTH), 
        (LONG)(WINDOW_HEIGHT)
    };
    AdjustWindowRect(
        &rect,                                    // クライアント矩形
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,  // ウィンドウスタイル
        FALSE                                     // メニューフラグ
    );
    hwnd = CreateWindow(_T(CLASS_NAME),
        _T(PROC_NAME),
        WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        rect.right - rect.left,           // ウィンドウの幅
        rect.bottom - rect.top,           // ウィンドウの高さ
        NULL,
        NULL,
        hInstance,
        NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    DX12Renderer*   renderer = new DX12Renderer(hwnd, WINDOW_WIDTH, WINDOW_HEIGHT); //初期化<追加>
    do {
        if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        renderer->Render(); //描画 <追加>
    } while (msg.message != WM_QUIT);
    delete  renderer;   //消去<追加>
    return (int)(msg.wParam);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return(DefWindowProc(hwnd, msg, wParam, lParam));
    }
    return (0L);
}

■Step7:ライブラリの追加

そのままではDX12はリンクしていないのでライブラリを追加します。
「test01」を右クリックしてサブメニューがでたら「プロパティ(R)」を選択してください。
2018-12-06 (12).png
「リンカー」「入力」の「追加の依存ファイル」にライブラリを追加します。
(このときX64になっていることを確認してください)
2018-12-06 (13).png
「d3d12.lib」「dxgi.lib」「dxguid.lib」を追加し「OK」を押します。・
2018-12-06 (14).png

■Step8:実行

実行すると以下のようになります。
2018-12-06 (15).png

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11