DirectX12 ④ インデックスバッファの作成

インデックスバッファの作成 

GPUにインデックスのデータを渡すためのバッファ。

環境

言語 : C++
開発環境 : Visual Studio 2020
OS : Windows10
CPU : AMD Ryzen5 3600
RAM : 24.0GB
GPU : NVIDIA GeForce GTX 1050

ソースコード

IndexBuffer.h

#pragma once

#include <d3d12.h>
#include <cstdint>
#include <wrl/client.h>
#include <iostream>

#include "Dx.h"

#pragma comment(lib, "d3d12.lib")

using namespace Microsoft::WRL;

class IndexBuffer {
public:
    IndexBuffer();
    bool CreateIndexBuffer(size_t size, uint32_t* data);
    D3D12_INDEX_BUFFER_VIEW GetIndexBufferView();

private:
    ComPtr<ID3D12Resource> _pBuffer;
    D3D12_INDEX_BUFFER_VIEW _indexBufferView;
};

IndexBuffer.cpp

#include "IndexBuffer.h"

//初期化
IndexBuffer::IndexBuffer() {
    _indexBufferView = {};
}

//インデックスバッファの作成
bool IndexBuffer::CreateIndexBuffer(size_t size, uint32_t* data) {
    //ヒーププロパティ
    D3D12_HEAP_PROPERTIES heapProp = {};
    heapProp.Type = D3D12_HEAP_TYPE_UPLOAD;     //GPUにアップロード
    heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    heapProp.CreationNodeMask = 0;
    heapProp.VisibleNodeMask = 0;

    //リソースの設定(バッファ用)
    D3D12_RESOURCE_DESC desc = {};
    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;   //バッファ用
    desc.Alignment = 0; //配置
    desc.Width = size;  //リソースの幅(データのサイズ)
    desc.Height = 1;    //リソースの高さ
    desc.DepthOrArraySize = 1;  //深さ
    desc.MipLevels = 1;         //ミップマップはしない
    desc.Format = DXGI_FORMAT_UNKNOWN;  //形式は指定しない
    desc.SampleDesc.Count = 1;  //サンプリングはしない
    desc.SampleDesc.Quality = 0;
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    desc.Flags = D3D12_RESOURCE_FLAG_NONE;

    //リソースの作成
    HRESULT hResult = dx->GetDevice()->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(_pBuffer.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create IndexBuffer" << std::endl;
        return false;
    }

    //インデックスバッファビュー
    _indexBufferView.BufferLocation = _pBuffer->GetGPUVirtualAddress(); //アドレス
    _indexBufferView.Format = DXGI_FORMAT_R32_UINT;     //形式はunsigned int 32ビット
    _indexBufferView.SizeInBytes = static_cast<uint32_t>(size); //データのサイズ

    //マッピング
    if (data != nullptr) {
        void* ptr = nullptr;
        hResult = _pBuffer->Map(0, nullptr, &ptr);
        if (FAILED(hResult)) {
            std::cout << "Failed mapping IndexBuffer" << std::endl;
            return false;
        }
        memcpy(ptr, &data[0], size);    //コピー
        _pBuffer->Unmap(0, nullptr);
    }

    return true;
}

//インデックスバッファビューの取得
D3D12_INDEX_BUFFER_VIEW IndexBuffer::GetIndexBufferView() {
    return _indexBufferView;
}

参考資料

書籍

サイト

qiita.com

learn.microsoft.com

DirectX12 ③ 頂点バッファの作成

頂点バッファの作成 

GPUに頂点のデータを渡すためのバッファ。

環境

言語 : C++
開発環境 : Visual Studio 2020
OS : Windows10
CPU : AMD Ryzen5 3600
RAM : 24.0GB
GPU : NVIDIA GeForce GTX 1050

ソースコード

VertexBuffer.h

#pragma once

#include <d3d12.h>
#include <cstdint>
#include <wrl/client.h>
#include <iostream>

#include "Dx.h"

#pragma comment(lib, "d3d12.lib")

class VertexBuffer {
public:
    VertexBuffer();
    bool CreateVertexBuffer(size_t size, size_t stride, const void* data);
    D3D12_VERTEX_BUFFER_VIEW GetVertexBufferView() const;

private:
    ComPtr<ID3D12Resource> _pVertexBuffer;
    D3D12_VERTEX_BUFFER_VIEW _vertexBufferView;
};

VertexBuffer.cpp

#include "VertexBuffer.h"

//初期化
VertexBuffer::VertexBuffer() {
    _vertexBufferView = {};
}

//頂点バッファの作成
bool VertexBuffer::CreateVertexBuffer(size_t size, size_t stride, const void* data) {
    //ヒーププロパティ
    D3D12_HEAP_PROPERTIES heapProp= {};
    heapProp.Type = D3D12_HEAP_TYPE_UPLOAD;                     //GPUにアップロード
    heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; 
    heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    heapProp.CreationNodeMask = 0;
    heapProp.VisibleNodeMask = 0;

    //リソースの設定(バッファ)
    D3D12_RESOURCE_DESC desc = {};
    desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;   //バッファ用
    desc.Alignment = 0;         //配置
    desc.Width = size;          //リソースの幅
    desc.Height = 1;            //リソースの高さ
    desc.DepthOrArraySize = 1;  //深さ
    desc.MipLevels = 1;         //ミップマップのレベル
    desc.Format = DXGI_FORMAT_UNKNOWN;  //形式は指定しない
    desc.SampleDesc.Count = 1;  //サンプリングはしない
    desc.SampleDesc.Quality = 0;
    desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    desc.Flags = D3D12_RESOURCE_FLAG_NONE;

    //頂点バッファを作成
    HRESULT hResult;
    hResult = dx->GetDevice()->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(_pVertexBuffer.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create VertexBuffer" << std::endl;
        return false;
    }

    //マッピング
    void* ptr = nullptr;
    hResult = _pVertexBuffer->Map(0, nullptr, &ptr);
    if (FAILED(hResult)) {
        std::cout << "Failed mapping VertexBuffer" << std::endl;
        return false;
    }
    memcpy(ptr, data, size);    //コピー
    _pVertexBuffer->Unmap(0, nullptr);

    _vertexBufferView.BufferLocation = _pVertexBuffer->GetGPUVirtualAddress();  //アドレス
    _vertexBufferView.SizeInBytes = static_cast<uint32_t>(size);        //頂点データのサイズ
    _vertexBufferView.StrideInBytes = static_cast<uint32_t>(stride);    //1頂点あたりのデータサイズ

    return true;
}

//頂点バッファビューの取得
D3D12_VERTEX_BUFFER_VIEW VertexBuffer::GetVertexBufferView() const {
    return _vertexBufferView;
}

参考資料

書籍

サイト

qiita.com

qiita.com

learn.microsoft.com

DirectX12 ② 初期化

DirectX12の初期化
言語 : C++
開発環境 : Visual Studio 2020
OS : Windows10
CPU : AMD Ryzen5 3600
RAM : 24.0GB
GPU : NVIDIA GeForce GTX 1050

画面のクリアまで

ヘッダファイル

Dx.h

#pragma once

#include <dxgi1_4.h>
#include <d3d12.h>
#include <wrl/client.h>
#include <cstdint>
#include <iostream>
#include <vector>

#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")

using namespace Microsoft::WRL;

constexpr uint32_t FRAME_BUFFER_COUNT = 2;

class Dx {
public:
    Dx();
    bool Init(uint32_t width, uint32_t height, HWND hwnd);
    void Terminate();

    void BeginRender();
    void EndRender();

    void ResetCmdList();
    void ExcuteCmdList();

    ID3D12Device* GetDevice();
    ID3D12GraphicsCommandList* GetCmdList();

    uint32_t GetWindowWidth();
    uint32_t GetWindowHeight();
    uint32_t GetCurrentBackBufferIndex();

private:
    void Present(uint32_t interval);
    void WaitGPU();

    bool CreateFactory();
    bool CreateDevice();
    bool CreateCommandQueue();
    bool CreateSwapChain();
    bool CreateCommandAllocator();
    bool CreateCommandList();
    bool CreateRenderTarget();
    bool CreateFence();
    bool CreateDepthStencil();

    void CreateViewPort();
    void CreateScissorRect();

private:
    HWND _hwnd;
    uint32_t _width, _height;   //ウィンドウの高さと幅

    ComPtr<IDXGIFactory4> _pFactory;    //ファクトリー
    ComPtr<ID3D12Device> _pDevice;  //デバイス
    ComPtr<ID3D12CommandQueue> _pCmdQueue;  //コマンドキュー
    ComPtr<IDXGISwapChain3> _pSwapChain;    //スワップチェイン
    ComPtr<ID3D12Resource> _pRenderTargets[FRAME_BUFFER_COUNT]; //レンダーターゲット
    ComPtr<ID3D12CommandAllocator> _pCmdAllocator[FRAME_BUFFER_COUNT];  //コマンドアロケーター
    ComPtr<ID3D12GraphicsCommandList> _pCmdList;    //コマンドリスト
    ComPtr<ID3D12DescriptorHeap> _pHeapRTV; //レンダーターゲット用のディスクリプタヒープ
    ComPtr<ID3D12RootSignature> _pRootSignature;    //ルートシグネチャ
    ComPtr<ID3D12PipelineState> _pPipelineState;    //パイプラインステート
    ComPtr<ID3D12Fence> _pFence;    //フェンス
    ComPtr<ID3D12DescriptorHeap> _pHeapDSV; //デプスステンシルビュー用のディスクリプタヒープ
    ComPtr<ID3D12Resource> _pDepthStencilBuffer;    //デプスステンシルバッファ

    uint32_t _currentBackBufferIndex;   //バックバッファーのインデックス
    HANDLE _fenceEvent; //フェンスイベント
    uint64_t _fenceCount[FRAME_BUFFER_COUNT];   //フェンスカウント
    D3D12_CPU_DESCRIPTOR_HANDLE _handleRTV[FRAME_BUFFER_COUNT]; //レンダーターゲットビューのハンドル
    D3D12_CPU_DESCRIPTOR_HANDLE _handleDSV; //デプスステンシルビューのハンドル

    D3D12_VIEWPORT _viewPort;
    D3D12_RECT _scissorRect;

};

extern Dx* dx;  //グローバル変数

コンストラクタと初期化関数

Dx.cpp

Dx* dx;

Dx::Dx() {
    _hwnd = nullptr;
    _currentBackBufferIndex = 0;
    _fenceEvent = nullptr;
    _handleDSV = {};
    _height = 0;
    _width = 0;
    _scissorRect = {};
    _viewPort = {};


    for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) {
        _fenceCount[i] = 0;
        _handleRTV[i] = {};
    }
}


bool Dx::Init(uint32_t width, uint32_t height, HWND hwnd) {
    HRESULT hResult;
#if defined(DEBUG) || defined(_DEBUG)
    {
        ComPtr<ID3D12Debug> debug;
        hResult = D3D12GetDebugInterface(IID_PPV_ARGS(debug.GetAddressOf()));

        if (SUCCEEDED(hResult)) {
            debug->EnableDebugLayer();
        }
    }
#endif

    _hwnd = hwnd;
    _width = width;
    _height = height;

    if (!CreateFactory()) {
        return false;
    }

    if (!CreateDevice()) {
        return false;
    }
    
    if (!CreateCommandQueue()) {
        return false;
    }

    if (!CreateSwapChain()) {
        return false;
    }

    if (!CreateCommandAllocator()) {
        return false;
    }

    if (!CreateCommandList()) {
        return false;
    }

    if (!CreateRenderTarget()) {
        return false;
    }

    if (!CreateDepthStencil()) {
        return false;
    }

    if (!CreateFence()) {
        return false;
    }

    CreateViewPort();
    CreateScissorRect();
    
    _pCmdList->Close(); 
    
    return true;
}

バイスの作成(NVIDIAのグラボを使用する場合)

Dx.cpp

//DXGIファクトリーの作成
bool Dx::CreateFactory() {
    HRESULT hResult;
    hResult = CreateDXGIFactory1(IID_PPV_ARGS(_pFactory.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create DXGIFactory" << std::endl;
        return false;
    }
    return true;
}

//デバイスの作成
bool Dx::CreateDevice() {
    HRESULT hResult;

    //アダプター列挙
    std::vector <IDXGIAdapter*> adapters;
    IDXGIAdapter* tmpAdapter = nullptr;
    for (uint32_t i = 0; _pFactory->EnumAdapters(i, &tmpAdapter) != DXGI_ERROR_NOT_FOUND; ++i) {
        adapters.push_back(tmpAdapter);
    }


    //アダプターの選択
    for (auto adpt : adapters) {
        DXGI_ADAPTER_DESC adesc = {};
        adpt->GetDesc(&adesc);
        std::wstring strDesc = adesc.Description;
        //NVIDIAのグラボを探す
        if (strDesc.find(L"NVIDIA") != std::string::npos) {
            tmpAdapter = adpt;
            break;
        }
    }

    //フィーチャレベル列挙
    D3D_FEATURE_LEVEL levels[] = {
        D3D_FEATURE_LEVEL_12_1,
        D3D_FEATURE_LEVEL_12_0,
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
    };

    //デバイスの作成
    //フィーチャーレベルを探す
    for (D3D_FEATURE_LEVEL l : levels) {
        hResult = D3D12CreateDevice(tmpAdapter, l, IID_PPV_ARGS(&_pDevice));
        if (hResult == S_OK) {
            break;
        }
    }
    if (hResult != S_OK) {
        std::cout << "Failed to create Device" << std::endl;
        return false;
    }

    tmpAdapter->Release(); //使い終わったらリリース

    return true;
}

バイスの作成(グラボを指定しない場合)

Dx.cpp

//DXGIファクトリーの作成
bool Dx::CreateFactory() {
    HRESULT hResult;
    hResult = CreateDXGIFactory1(IID_PPV_ARGS(_pFactory.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create DXGIFactory" << std::endl;
        return false;
    }
    return true;
}

//デバイスの作成
bool Dx::CreateDevice() {
    HRESULT hResult;

    //フィーチャレベル列挙
    D3D_FEATURE_LEVEL levels[] = {
        D3D_FEATURE_LEVEL_12_1,
        D3D_FEATURE_LEVEL_12_0,
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
    };

    //デバイスの作成
    //フィーチャーレベルを探す
    for (D3D_FEATURE_LEVEL l : levels) {
        hResult = D3D12CreateDevice(nullptr, l, IID_PPV_ARGS(&_pDevice));
        if (hResult == S_OK) {
            break;
        }
    }
    if (hResult != S_OK) {
        std::cout << "Failed to create Device" << std::endl;
        return false;
    }

    return true;
}

コマンドキューの作成

Dx.cpp

//コマンドキューの作成
bool Dx::CreateCommandQueue() {
    D3D12_COMMAND_QUEUE_DESC cmdQueueDesc = {};
    cmdQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    cmdQueueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
    cmdQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    cmdQueueDesc.NodeMask = 0;

    HRESULT hResult = _pDevice->CreateCommandQueue(&cmdQueueDesc, IID_PPV_ARGS(_pCmdQueue.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create CommnadQueue" << std::endl;
        return false;
    }

    return true;
}

スワップチェインの作成

Dx.cpp

//スワップチェインの作成
bool Dx::CreateSwapChain() {
    DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
    swapChainDesc.BufferDesc.Width = _width;
    swapChainDesc.BufferDesc.Height = _height;
    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDesc.SampleDesc.Count = 1;
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = FRAME_BUFFER_COUNT;
    swapChainDesc.OutputWindow = _hwnd;
    swapChainDesc.Windowed = TRUE;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

    ComPtr<IDXGISwapChain> swapChain;
    HRESULT hResult = _pFactory->CreateSwapChain(_pCmdQueue.Get(), &swapChainDesc, swapChain.GetAddressOf());
    if (FAILED(hResult)) {
        std::cout << "Failed to create IDXGISwapChain" << std::endl;
        return false;
    }

    //IDXGISwapChain3を取得
    hResult = swapChain.As(&_pSwapChain);
    if (FAILED(hResult)) {
        std::cout << "Failed to get IDXGISwapChain3" << std::endl;
        return false;
    }

    //バックバッファ番号を取得
    _currentBackBufferIndex = _pSwapChain->GetCurrentBackBufferIndex();

    swapChain.Reset();  //解放

    return true;
}

コマンドアロケータの作成

Dx.cpp

//コマンドアロケータの作成
bool Dx::CreateCommandAllocator() {
    HRESULT hResult;
    for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) {
        hResult = _pDevice->CreateCommandAllocator(
            D3D12_COMMAND_LIST_TYPE_DIRECT,
            IID_PPV_ARGS(_pCmdAllocator[i].GetAddressOf()));
        if (FAILED(hResult)) {
            std::cout << "Failed to create CommandAllocator" << std::endl;
            return false;
        }
    }

    return true;
}

コマンドリストの作成

Dx.cpp

//コマンドリストの作成
bool Dx::CreateCommandList() {
    HRESULT hResult = _pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, _pCmdAllocator[_currentBackBufferIndex].Get(), nullptr, IID_PPV_ARGS(_pCmdList.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create CommandList" << std::endl;
        return false;
    }
    return true;
}

レンダーターゲットビューの作成

Dx.cpp

//レンダーターゲットビューの作成
bool Dx::CreateRenderTarget() {
    //ディスクリプタヒープの作成
    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
    heapDesc.NumDescriptors = FRAME_BUFFER_COUNT;
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    heapDesc.NodeMask = 0;

    HRESULT hResult = _pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(_pHeapRTV.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create DescriptorHeap" << std::endl;
        return false;
    }

    D3D12_CPU_DESCRIPTOR_HANDLE handle = _pHeapRTV->GetCPUDescriptorHandleForHeapStart();
    uint32_t incrementSize = _pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);

    for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) {
        hResult = _pSwapChain->GetBuffer(i, IID_PPV_ARGS(_pRenderTargets[i].GetAddressOf()));
        if (FAILED(hResult)) {
            std::cout << "Failed to get RenderTargets" << std::endl;
            return false;
        }

        D3D12_RENDER_TARGET_VIEW_DESC viewDesc = {};
        viewDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
        viewDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
        viewDesc.Texture2D.MipSlice = 0;
        viewDesc.Texture2D.PlaneSlice = 0;

        _pDevice->CreateRenderTargetView(_pRenderTargets[i].Get(), &viewDesc, handle);

        _handleRTV[i] = handle;
        handle.ptr += incrementSize;
    }

    return true;
}

フェンスの作成

Dx.cpp

//フェンスの作成
bool Dx::CreateFence() {
    for (uint32_t i = 0; i < FRAME_BUFFER_COUNT; i++) {
        _fenceCount[i] = 0;
    }
    HRESULT hResult = _pDevice->CreateFence(_fenceCount[_currentBackBufferIndex], D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(_pFence.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create Fence" << std::endl;
        return false;
    }

    _fenceCount[_currentBackBufferIndex]++;

    //イベントの作成
    _fenceEvent = CreateEvent(nullptr, false, false, nullptr);
    if (_fenceEvent == nullptr) {
        return false;
    }

    return true;
}

デプスステンシルビューの作成

Dx.cpp

//デプスステンシルビューの作成
bool Dx::CreateDepthStencil() {
    D3D12_HEAP_PROPERTIES heapProp = {};
    heapProp.Type = D3D12_HEAP_TYPE_DEFAULT;
    heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    heapProp.CreationNodeMask = 1;
    heapProp.VisibleNodeMask = 1;

    D3D12_RESOURCE_DESC desc = {};
    desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    desc.Alignment = 0;
    desc.Width = _width;
    desc.Height = _height;
    desc.DepthOrArraySize = 1;
    desc.MipLevels = 1;
    desc.Format = DXGI_FORMAT_D32_FLOAT;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

    D3D12_CLEAR_VALUE clearValue;
    clearValue.Format = DXGI_FORMAT_D32_FLOAT;
    clearValue.DepthStencil.Depth = 1.0f;
    clearValue.DepthStencil.Stencil = 0;

    HRESULT hResult = _pDevice->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue, IID_PPV_ARGS(_pDepthStencilBuffer.GetAddressOf()));
    if (FAILED(hResult)) {
        std::cout << "Failed to create DepthStencilBuffer" << std::endl;
        return false;
    }

    D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};
    heapDesc.NumDescriptors = 1;
    heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
    heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    heapDesc.NodeMask = 0;

    hResult = _pDevice->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&_pHeapDSV));
    if (FAILED(hResult)) {
        std::cout << "Failed to create DescriptorHeap for DepthStencilView " << std::endl;
        return false;
    }

    D3D12_CPU_DESCRIPTOR_HANDLE handle = _pHeapDSV->GetCPUDescriptorHandleForHeapStart();
    uint32_t incrementSize = _pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);

    D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = {};
    viewDesc.Format = DXGI_FORMAT_D32_FLOAT;
    viewDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
    viewDesc.Texture2D.MipSlice = 0;
    viewDesc.Flags = D3D12_DSV_FLAG_NONE;

    _pDevice->CreateDepthStencilView(_pDepthStencilBuffer.Get(), &viewDesc, handle);
    _handleDSV = handle;

    return true;
}

ビューポート、シザー矩形の作成

Dx.cpp

//ビューポートの作成
void Dx::CreateViewPort() {
    _viewPort.TopLeftX = 0;
    _viewPort.TopLeftY = 0;
    _viewPort.Width = static_cast<float>(_width);
    _viewPort.Height = static_cast<float>(_height);
    _viewPort.MinDepth = 0.0f;
    _viewPort.MaxDepth = 1.0f;
}

//シザー矩形の作成
void Dx::CreateScissorRect() {
    _scissorRect.left = 0;
    _scissorRect.right = _width;
    _scissorRect.top = 0;
    _scissorRect.bottom = _height;
}

終了処理

Dx.cpp

//終了処理
void Dx::Terminate() {
    WaitGPU();

    if (_fenceEvent != nullptr) {
        CloseHandle(_fenceEvent);
        _fenceEvent = nullptr;
    }

    _pHeapRTV.Reset();
}

//GPUの処理待機
void Dx::WaitGPU() {
    //シグナル処理
    _pCmdQueue->Signal(_pFence.Get(), _fenceCount[_currentBackBufferIndex]);
    //イベントを設定
    _pFence->SetEventOnCompletion(_fenceCount[_currentBackBufferIndex], _fenceEvent);
    //待機処理
    WaitForSingleObjectEx(_fenceEvent, INFINITE, false);

    _fenceCount[_currentBackBufferIndex]++;
}

レンダリング

Dx.cpp

//描画開始
void Dx::BeginRender() {
    //コマンドの記録開始
    _pCmdAllocator[_currentBackBufferIndex]->Reset();
    _pCmdList->Reset(_pCmdAllocator[_currentBackBufferIndex].Get(), nullptr);

    //リソースバリア
    D3D12_RESOURCE_BARRIER barrier = {};
    barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barrier.Transition.pResource = _pRenderTargets[_currentBackBufferIndex].Get();
    barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
    barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
    barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    _pCmdList->ResourceBarrier(1, &barrier);

    //レンダーターゲットの設定
    _pCmdList->OMSetRenderTargets(1, &_handleRTV[_currentBackBufferIndex], false, &_handleDSV);

    //クリアカラー(赤)
    float clearColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };

    //レンダーターゲットビューをクリア
    _pCmdList->ClearRenderTargetView(_handleRTV[_currentBackBufferIndex], clearColor, 0, nullptr);

    //デプスステンシルバッファクリア
    _pCmdList->ClearDepthStencilView(_handleDSV, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);

    //ビューポートとシザー矩形をセット
    _pCmdList->RSSetViewports(1, &_viewPort);
    _pCmdList->RSSetScissorRects(1, &_scissorRect);

}

//描画終了
void Dx::EndRender() {
    //リソースバリア
    D3D12_RESOURCE_BARRIER barrier = {};
    barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
    barrier.Transition.pResource = _pRenderTargets[_currentBackBufferIndex].Get();
    barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
    barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
    barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    _pCmdList->ResourceBarrier(1, &barrier);

    //コマンドリストを閉じる
    _pCmdList->Close();

    //コマンドを実行
    ID3D12CommandList* ppCmdLists[] = { _pCmdList.Get() };
    _pCmdQueue->ExecuteCommandLists(1, ppCmdLists);

    //画面に表示
    Present(1);
}

//コマンドリストのリセット
void Dx::ResetCmdList() {
    //_pCmdAllocator->Reset();
    _pCmdList->Reset(_pCmdAllocator->Get(), nullptr);
}

//コマンドリストの実行
void Dx::ExcuteCmdList() {
    ID3D12CommandList* ppCmdLists[] = { _pCmdList.Get() };
    _pCmdQueue->ExecuteCommandLists(1, ppCmdLists);

    //シグナル処理
    uint64_t currentValue = _fenceCount[_currentBackBufferIndex];
    _pCmdQueue->Signal(_pFence.Get(), currentValue);

    if (_pFence->GetCompletedValue() != currentValue) {
        auto event = CreateEvent(nullptr, false, false, nullptr);
        _pFence->SetEventOnCompletion(currentValue, event);
        WaitForSingleObject(event, INFINITE);
        CloseHandle(event);
    }

    _pCmdAllocator[_currentBackBufferIndex]->Reset();
    _pCmdList->Reset(_pCmdAllocator[_currentBackBufferIndex].Get(), nullptr);
}

//表示
void Dx::Present(uint32_t interval) {
    //画面に表示
    _pSwapChain->Present(interval, 0);

    //シグナル処理
    uint64_t currentValue = _fenceCount[_currentBackBufferIndex];
    _pCmdQueue->Signal(_pFence.Get(), currentValue);

    //バックバッファ番号を更新
    _currentBackBufferIndex = _pSwapChain->GetCurrentBackBufferIndex();


    //描画待機
    if (_pFence->GetCompletedValue() < _fenceCount[_currentBackBufferIndex]) {
        _pFence->SetEventOnCompletion(_fenceCount[_currentBackBufferIndex], _fenceEvent);
        WaitForSingleObjectEx(_fenceEvent, INFINITE, false);
    }

    _fenceCount[_currentBackBufferIndex] = currentValue + 1;
}

getter関数

Dx.cpp

//デバイスの取得
ID3D12Device* Dx::GetDevice() {
    return _pDevice.Get();
}

//コマンドリストの取得
ID3D12GraphicsCommandList* Dx::GetCmdList() {
    return _pCmdList.Get();
}

//ウィンドウの高さの取得
uint32_t Dx::GetWindowWidth() {
    return _width;
}

//ウィンドウの幅の取得
uint32_t Dx::GetWindowHeight() {
    return _height;
}

//バックバッファのインデックスを取得
uint32_t Dx::GetCurrentBackBufferIndex() {
    return _currentBackBufferIndex;
}

実行結果

クリアカラーで赤を指定しているため、赤い画面が表示されている。

参考資料

書籍

サイト

qiita.com

learn.microsoft.com

DirectX12 ① ウィンドウの作成

開発環境

言語 : C++
開発環境 : Visual Studio 2020
OS : Windows10
CPU : AMD Ryzen5 3600
RAM : 24.0GB
GPU : NVIDIA GeForce GTX 1050

DirectX12について

ここでは、Direct3D 12のことをDirectX12と呼ぶ。DirectX12は、Microsoftが開発する、DirectX12互換GPUを搭載したPCにおいてグラフィックス機能や計算機能をアプリケーションで利用すためのAPIである。

Win32 APIについて

ウィンドウの表示にはWin32 APIを使う。
Win32 APIは、ウィンドウの描画などWindowsの機能を使用するためのAPIである。
DirectX12はOpenGLのようにglutやGLFWなどの簡単にウィンドウを表示するための機能がないため、Win32 APIによってウィンドウの表示を行う。



main.cpp

#include "App.h"

int main() {
    App app(1080, 720);
    app.Run();
}

App.h

#pragma once

#include <Windows.h>
#include <cstdint>
#include <string>

class App {
public:
    App(uint32_t width, uint32_t height);

    void Run();

private:
    bool InitApp();
    bool InitWindow();

    void MainLoop();

    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

private:
    const std::wstring WindowClassName = L"WindowClass";

    uint32_t _width;
    uint32_t _height;

    HWND _hwnd;
    HINSTANCE _hInstance;

};

App.cpp

#include "App.h"

App::App(uint32_t width, uint32_t height) {
    _width = width;
    _height = height;
    _hwnd = nullptr;
    _hInstance = nullptr;
}

void App::Run() {
    if (!InitApp()) {
        return;
    }

    MainLoop();
}

//初期化
bool App::InitApp() {

    if (!InitWindow()) {
        return false;
    }

    return true;
}

//ウィンドウの初期化
bool App::InitWindow() {
    _hInstance = GetModuleHandle(nullptr);
    if (_hInstance == nullptr) {
        return false;
    }

    //ウィンドウの設定
    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;     //水平方向と垂直方向のサイズ変更で再描画
    wc.lpfnWndProc = WndProc;   //ウィンドウプロシージャの登録
    wc.hIcon = LoadIcon(_hInstance, IDI_APPLICATION);    //アイコン
    wc.hCursor = LoadCursor(_hInstance, IDC_ARROW);      //マウスカーソル
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //背景色は黒
    wc.hInstance = _hInstance;  //インスタンスハンドル
    wc.lpszMenuName = nullptr;  //メニュー
    wc.lpszClassName = WindowClassName.c_str();      //ウィンドウクラスの名前
    wc.hIconSm = LoadIcon(_hInstance, IDI_APPLICATION);      //小さいアイコン

    RegisterClassEx(&wc);    //ウィンドウクラスの登録

    //ウィンドウサイズ
    RECT rc = {};
    rc.right = static_cast<long>(_width);    //ウィンドウの幅
    rc.bottom = static_cast<long>(_height); //ウィンドウの高さ

    auto style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
    AdjustWindowRect(&rc, style, false);

    _hwnd = CreateWindowEx(
        0,
        WindowClassName.c_str(),
        L"Title",
        style,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rc.right - rc.left, rc.bottom - rc.top,
        nullptr, nullptr,
        _hInstance,
        nullptr
    );

    ShowWindow(_hwnd, SW_SHOWNORMAL);

    SetFocus(_hwnd);

    return true;
}

//メインループ
void App::MainLoop() {
    MSG msg = {};
    while (WM_QUIT != msg.message)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE == true))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            // ここに描画処理を書く


        }
    }
}

//ウィンドウプロシージャ
LRESULT CALLBACK App::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        break;
    }

    return DefWindowProc(hwnd, msg, wParam, lParam);
}

実行結果