示例三角形绘制
1.效果
下面只贴出关于dx的代码,有时间再详细说明。
2.标头.h
#pragma once
#include "pch.h"
#include "LVEDebug.h"
#include "LVESystem.h"
#include <d3d12.h>
#include "d3dx12.h"
#include <dxgi1_6.h>
#include <D3DCompiler.h>
#include <DirectXMath.h>
using namespace DirectX;
typedef IDXGIFactory7 _IDXGIFactory;
typedef IDXGIAdapter4 _IDXGIAdapter;
typedef IDXGISwapChain4 _IDXGISwapChain;
namespace LVE
{
void GetHardwareAdapter(_IDXGIFactory* pFactory, _IDXGIAdapter** ppAdapter);
const int SWAP_CHAIN_BUFFER_COUNT = 2;//此值不能小于2
class DirectX
{
public:
//pipeline
com_ptr<_IDXGIFactory> _factory;
com_ptr<_IDXGIAdapter> _adapter;
com_ptr<ID3D12Device> _device;
com_ptr<ID3D12CommandQueue> _commandQueue;
com_ptr<_IDXGISwapChain> _swapChain;
com_ptr<ID3D12DescriptorHeap> _rtvHeap;
UINT _rtvDescriptorSize;
com_ptr<ID3D12Resource> _renderTargets[SWAP_CHAIN_BUFFER_COUNT];
com_ptr<ID3D12CommandAllocator> _commandAllocator;
//assets
com_ptr<ID3D12RootSignature> _rootSignature;
com_ptr<ID3D12PipelineState> _pipelineState;
com_ptr<ID3D12GraphicsCommandList> _commandList;
com_ptr<ID3D12Resource> _vertexBuffer;
D3D12_VERTEX_BUFFER_VIEW _vertexBufferView;
//Synchronization objects.
UINT _frameIndex;
HANDLE _fenceEvent;
com_ptr<ID3D12Fence> _fence;
UINT64 _fenceValue;
D3D12_VIEWPORT _viewport;
D3D12_RECT _scissorRect;
void Init();
void Update();
void Render();
void Release();
private:
struct Vertex
{
XMFLOAT3 position;
XMFLOAT4 color;
};
void _init_pre()
{
if (!XMVerifyCPUSupport())
{
g_debug.Line(L"DirectXMath does not support the current platform!");
return;
}
}
void _init_pipeline()
{
#if defined(_DEBUG)
_init_debug_layer();
#endif
_init_dxgi();
_init_device();
_init_command_queue();
_init_swap_chain();
_init_descriptor_heap();
_init_render_target_view();
_init_command_allocator();
}
void _init_debug_layer();
void _init_dxgi();
void _init_device();
void _init_command_queue();
void _init_swap_chain();
void _init_descriptor_heap();
void _init_render_target_view();
void _init_command_allocator();
//
void _init_assets()
{
_init_root_signature();
_init_graphics_pipeline_state();
_init_command_list();
_init_vertex_buffer();
_init_fence();
}
void _init_root_signature();
void _init_graphics_pipeline_state();
void _init_command_list();
void _init_vertex_buffer();
void _init_fence();
void WaitForPreviousFrame();
void PopulateCommandList();
};
extern DirectX g_dx;
}
3.源.cpp
#include "LVEDirectX.h"
namespace LVE
{
DirectX g_dx;
void DirectX::Init()
{
_init_pre();
//load pipeline
_init_pipeline();
//load assets
_init_assets();
_viewport.Height = 600;
_viewport.MaxDepth = 1.0f;
_viewport.MinDepth = 0;
_viewport.TopLeftX = 0;
_viewport.TopLeftY = 0;
_viewport.Width = 800;
_scissorRect.bottom = 600;
_scissorRect.left = 0;
_scissorRect.right = 800;
_scissorRect.top = 0;
}
void DirectX::Update()
{
}
void DirectX::Render()
{
// Record all the commands we need to render the scene into the command list.
PopulateCommandList();
// Execute the command list.
ID3D12CommandList* ppCommandLists[] = { _commandList.get() };
_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
// Present the frame.
HRESULT hr = _swapChain->Present(1, 0);
if (FAILED(hr))
{
g_debug.Line(L"Failed to present frame!");
return;
}
WaitForPreviousFrame();
}
void DirectX::Release()
{
// Wait for the GPU to be done with all resources.
WaitForPreviousFrame();
CloseHandle(_fenceEvent);
}
void DirectX::_init_debug_layer()
{
// Enable the D3D12 debug layer.
com_ptr<ID3D12Debug> debugController;
HRESULT hr = D3D12GetDebugInterface(IID_PPV_ARGS(&debugController));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create debug layer!");
return;
}
debugController->EnableDebugLayer();
}
void DirectX::_init_dxgi()
{
HRESULT hr = CreateDXGIFactory1(__uuidof(_IDXGIFactory), (void**)(&_factory));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create DXGI factory!");
return;
}
}
void DirectX::_init_device()
{
_IDXGIAdapter* adapter;
GetHardwareAdapter(_factory.get(), &adapter);
if (adapter == nullptr)
{
g_debug.Line(L"Failed to enumerate hardware adapter!");
return;
}
_adapter.attach(adapter);
HRESULT hr = D3D12CreateDevice(
_adapter.get(),
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&_device));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create device!");
return;
}
}
void DirectX::_init_command_queue()
{
D3D12_COMMAND_QUEUE_DESC desc;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.NodeMask = 0;
desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
HRESULT hr = _device->CreateCommandQueue(&desc, IID_PPV_ARGS(&_commandQueue));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create command queue!");
return;
}
}
void DirectX::_init_swap_chain()
{
com_ptr<IDXGISwapChain> swapChain;
DXGI_SWAP_CHAIN_DESC desc;
desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BufferDesc.Height = 600;
desc.BufferDesc.RefreshRate.Denominator = 0;
desc.BufferDesc.RefreshRate.Numerator = 0;
desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
desc.BufferDesc.Width = 800;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.Flags = 0;
desc.OutputWindow = g_system.GetHwnd();
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.Windowed = TRUE;
HRESULT hr = _factory->CreateSwapChain(
_commandQueue.get(),
&desc,
swapChain.put()
);
/*
DXGI_SWAP_CHAIN_DESC1 desc;
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
desc.BufferCount = SWAP_CHAIN_BUFFER_COUNT;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.Flags = 0;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.Height = 600;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Scaling = DXGI_SCALING_NONE;
desc.Stereo = FALSE;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.Width = 800;
//DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_full;
HRESULT hr = _factory->CreateSwapChainForHwnd(
_commandQueue.get(),// For Direct3D 12, this is a pointer to a direct command queue, and not to the device.
g_system.GetHwnd(),
&desc,
nullptr,
nullptr,
swapChain.put()
);
*/
if (FAILED(hr))
{
g_debug.Line(L"Failed to create swap chain!");
return;
}
swapChain.as(_swapChain);
}
void DirectX::_init_descriptor_heap()
{
D3D12_DESCRIPTOR_HEAP_DESC desc;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 0;
desc.NumDescriptors = SWAP_CHAIN_BUFFER_COUNT;//
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
HRESULT hr = _device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_rtvHeap));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create descriptor heap!");
return;
}
_rtvDescriptorSize = _device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
}
void DirectX::_init_render_target_view()
{
UINT m_rtvDescriptorSize = _device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// Create frame resources.
//D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = _rtvHeap->GetCPUDescriptorHandleForHeapStart();
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(_rtvHeap->GetCPUDescriptorHandleForHeapStart());
// Create a RTV for each frame.
for (UINT n = 0; n < SWAP_CHAIN_BUFFER_COUNT; n++)
{
HRESULT hr = _swapChain->GetBuffer(n, IID_PPV_ARGS(&_renderTargets[n]));
if (FAILED(hr))
{
g_debug.Line(L"Failed to get buffer from the swap chain!");
continue;
}
_device->CreateRenderTargetView(_renderTargets[n].get(), nullptr, rtvHandle);
rtvHandle.Offset(1, m_rtvDescriptorSize);
}
}
void DirectX::_init_command_allocator()
{
HRESULT hr = _device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&_commandAllocator));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create command allocator!");
return;
}
}
void DirectX::_init_root_signature()
{
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ID3DBlob* signature;
ID3DBlob* error;
HRESULT hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error);
if (FAILED(hr))
{
g_debug.Line(L"Failed to serialize root signature!");
return;
}
hr = _device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature));
if (FAILED(hr))
{
g_debug.Line(L"Failed to serialize root signature!");
return;
}
}
void DirectX::_init_graphics_pipeline_state()
{
com_ptr<ID3DBlob> vertexShader;
com_ptr<ID3DBlob> pixelShader;
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
HRESULT hr = D3DCompileFromFile(L"shaders.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, vertexShader.put(), nullptr);
if (FAILED(hr))
{
g_debug.Line(L"Failed to compile vertex shader!");
return;
}
hr = D3DCompileFromFile(L"shaders.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, pixelShader.put(), nullptr);
if (FAILED(hr))
{
g_debug.Line(L"Failed to compile pixel shader!");
return;
}
// Define the vertex input layout.
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 }
};
// Describe and create the graphics pipeline state object (PSO).
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
psoDesc.pRootSignature = _rootSignature.get();
psoDesc.VS = { reinterpret_cast<UINT8*>(vertexShader->GetBufferPointer()), vertexShader->GetBufferSize() };
psoDesc.PS = { reinterpret_cast<UINT8*>(pixelShader->GetBufferPointer()), pixelShader->GetBufferSize() };
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState.DepthEnable = FALSE;
psoDesc.DepthStencilState.StencilEnable = FALSE;
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.SampleDesc.Count = 1;
hr = _device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&_pipelineState));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create graphics pipeline state!");
return;
}
}
void DirectX::_init_command_list()
{
HRESULT hr = _device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, _commandAllocator.get(), _pipelineState.get(), IID_PPV_ARGS(&_commandList));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create command list!");
return;
}
_commandList->Close();
}
void DirectX::_init_vertex_buffer()
{
// Define the geometry for a triangle.
Vertex triangleVertices[] =
{
{ { 0.0f, 0.25f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
{ { 0.25f, -0.25f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { -0.25f, -0.25f, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }
};
const UINT vertexBufferSize = sizeof(triangleVertices);
// Note: using upload heaps to transfer static data like vert buffers is not
// recommended. Every time the GPU needs it, the upload heap will be marshalled
// over. Please read up on Default Heap usage. An upload heap is used here for
// code simplicity and because there are very few verts to actually transfer.
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);
auto desc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
HRESULT hr = _device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&_vertexBuffer));
if (FAILED(hr))
{
g_debug.Line(L"Failed to create vertex buffer!");
return;
}
// Copy the triangle data to the vertex buffer.
UINT8* pVertexDataBegin;
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
hr = _vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin));
memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
_vertexBuffer->Unmap(0, nullptr);
// Initialize the vertex buffer view.
_vertexBufferView.BufferLocation = _vertexBuffer->GetGPUVirtualAddress();
_vertexBufferView.StrideInBytes = sizeof(Vertex);
_vertexBufferView.SizeInBytes = vertexBufferSize;
}
void DirectX::_init_fence()
{
HRESULT hr = _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&_fence));
_fenceValue = 1;
// Create an event handle to use for frame synchronization.
_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (_fenceEvent == nullptr)
{
hr = HRESULT_FROM_WIN32(GetLastError());
if (FAILED(hr))
{
g_debug.Line(L"Failed to create fence!");
return;
}
}
// Wait for the command list to execute; we are reusing the same command
// list in our main loop but for now, we just want to wait for setup to
// complete before continuing.
WaitForPreviousFrame();
}
void DirectX::WaitForPreviousFrame()
{
// WAITING FOR THE FRAME TO COMPLETE BEFORE CONTINUING IS NOT BEST PRACTICE.
// This is code implemented as such for simplicity. More advanced samples
// illustrate how to use fences for efficient resource usage.
// Signal and increment the fence value.
const UINT64 fence = _fenceValue;
HRESULT hr = _commandQueue->Signal(_fence.get(), fence);
if (FAILED(hr))
{
g_debug.Line(L"Failed to signal and increment fence!");
return;
}
_fenceValue++;
// Wait until the previous frame is finished.
if (_fence->GetCompletedValue() < fence)
{
hr = _fence->SetEventOnCompletion(fence, _fenceEvent);
if (FAILED(hr))
{
g_debug.Line(L"Failed to set event on completion!");
return;
}
WaitForSingleObject(_fenceEvent, INFINITE);
}
_frameIndex = _swapChain->GetCurrentBackBufferIndex();
}
void DirectX::PopulateCommandList()
{
// Command list allocators can only be reset when the associated
// command lists have finished execution on the GPU; apps should use
// fences to determine GPU execution progress.
HRESULT hr = _commandAllocator->Reset();
if (FAILED(hr))
{
g_debug.Line(L"Failed to reset command allocator!");
return;
}
// However, when ExecuteCommandList() is called on a particular command
// list, that command list can then be reset at any time and must be before
// re-recording.
hr = _commandList->Reset(_commandAllocator.get(), _pipelineState.get());
if (FAILED(hr))
{
g_debug.Line(L"Failed to reset command list!");
return;
}
// Set necessary state.
_commandList->SetGraphicsRootSignature(_rootSignature.get());
_commandList->RSSetViewports(1, &_viewport);
_commandList->RSSetScissorRects(1, &_scissorRect);
// Indicate that the back buffer will be used as a render target.
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(_renderTargets[_frameIndex].get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
_commandList->ResourceBarrier(1, &barrier);
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(_rtvHeap->GetCPUDescriptorHandleForHeapStart(), _frameIndex, _rtvDescriptorSize);
_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
// Record commands.
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
_commandList->IASetVertexBuffers(0, 1, &_vertexBufferView);
_commandList->DrawInstanced(3, 1, 0, 0);
// Indicate that the back buffer will now be used to present.
barrier = CD3DX12_RESOURCE_BARRIER::Transition(_renderTargets[_frameIndex].get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
_commandList->ResourceBarrier(1, &barrier);
hr = _commandList->Close();
if (FAILED(hr))
{
g_debug.Line(L"Failed to close command list!");
return;
}
}
void GetHardwareAdapter(_IDXGIFactory* pFactory, _IDXGIAdapter** ppAdapter)
{
*ppAdapter = nullptr;
for (UINT adapterIndex = 0; ; ++adapterIndex)
{
_IDXGIAdapter* pAdapter = nullptr;
if (DXGI_ERROR_NOT_FOUND == pFactory->EnumAdapterByGpuPreference(
adapterIndex,
DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, _uuidof(_IDXGIAdapter), (void**)&pAdapter))
{
// No more adapters to enumerate.
break;
}
// Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
*ppAdapter = pAdapter;
return;
}
pAdapter->Release();
}
}
}