#include "stdafx.h"
using namespace DirectX;
struct Vertex {
Vertex(float x, float y, float z, float r, float g, float b, float a) : pos(x, y, z), color(r, g, b, z) {}
XMFLOAT3 pos;
XMFLOAT4 color;
};
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd)
{
if (!InitializeWindow(hInstance, nShowCmd, FullScreen))
{
MessageBox(0, L"Window Initialization - Failed",
L"Error", MB_OK);
return 1;
}
if (!InitD3D())
{
MessageBox(0, L"Failed to initialize direct3d 12",
L"Error", MB_OK);
Cleanup();
return 1;
}
mainloop();
WaitForPreviousFrame();
CloseHandle(fenceEvent);
Cleanup();
return 0;
}
bool InitializeWindow(HINSTANCE hInstance,
int ShowWnd,
bool fullscreen)
{
if (fullscreen)
{
HMONITOR hmon = MonitorFromWindow(hwnd,
MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = { sizeof(mi) };
GetMonitorInfo(hmon, &mi);
Width = mi.rcMonitor.right - mi.rcMonitor.left;
Height = mi.rcMonitor.bottom - mi.rcMonitor.top;
}
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = NULL;
wc.cbWndExtra = NULL;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2);
wc.lpszMenuName = NULL;
wc.lpszClassName = WindowName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Error registering class",
L"Error", MB_OK | MB_ICONERROR);
return false;
}
hwnd = CreateWindowEx(NULL,
WindowName,
WindowTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
Width, Height,
NULL,
NULL,
hInstance,
NULL);
if (!hwnd)
{
MessageBox(NULL, L"Error creating window",
L"Error", MB_OK | MB_ICONERROR);
return false;
}
if (fullscreen)
{
SetWindowLong(hwnd, GWL_STYLE, 0);
}
ShowWindow(hwnd, ShowWnd);
UpdateWindow(hwnd);
return true;
}
void mainloop() {
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (Running)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
Update();
Render();
}
}
}
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg)
{
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) {
if (MessageBox(0, L"Are you sure you want to exit?",
L"Really?", MB_YESNO | MB_ICONQUESTION) == IDYES)
{
Running = false;
DestroyWindow(hwnd);
}
}
return 0;
case WM_DESTROY:
Running = false;
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,
msg,
wParam,
lParam);
}
bool InitD3D()
{
HRESULT hr;
IDXGIFactory4* dxgiFactory;
hr = CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory));
if (FAILED(hr))
{
return false;
}
IDXGIAdapter1* adapter;
int adapterIndex = 0;
bool adapterFound = false;
while (dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
{
continue;
}
hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr);
if (SUCCEEDED(hr))
{
adapterFound = true;
break;
}
adapterIndex++;
}
if (!adapterFound)
{
return false;
}
hr = D3D12CreateDevice(
adapter,
D3D_FEATURE_LEVEL_11_0,
IID_PPV_ARGS(&device)
);
if (FAILED(hr))
{
return false;
}
D3D12_COMMAND_QUEUE_DESC cqDesc = {};
cqDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
cqDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
hr = device->CreateCommandQueue(&cqDesc, IID_PPV_ARGS(&commandQueue));
if (FAILED(hr))
{
return false;
}
DXGI_MODE_DESC backBufferDesc = {};
backBufferDesc.Width = Width;
backBufferDesc.Height = Height;
backBufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
DXGI_SAMPLE_DESC sampleDesc = {};
sampleDesc.Count = 1;
DXGI_SWAP_CHAIN_DESC swapChainDesc = {};
swapChainDesc.BufferCount = frameBufferCount;
swapChainDesc.BufferDesc = backBufferDesc;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.SampleDesc = sampleDesc;
swapChainDesc.Windowed = !FullScreen;
IDXGISwapChain* tempSwapChain;
dxgiFactory->CreateSwapChain(
commandQueue,
&swapChainDesc,
&tempSwapChain
);
swapChain = static_cast<IDXGISwapChain3*>(tempSwapChain);
frameIndex = swapChain->GetCurrentBackBufferIndex();
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = frameBufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
hr = device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvDescriptorHeap));
if (FAILED(hr))
{
return false;
}
rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
for (int i = 0; i < frameBufferCount; i++)
{
hr = swapChain->GetBuffer(i, IID_PPV_ARGS(&renderTargets[i]));
if (FAILED(hr))
{
return false;
}
device->CreateRenderTargetView(renderTargets[i], nullptr, rtvHandle);
rtvHandle.Offset(1, rtvDescriptorSize);
}
for (int i = 0; i < frameBufferCount; i++)
{
hr = device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator[i]));
if (FAILED(hr))
{
return false;
}
}
hr = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator[frameIndex], NULL, IID_PPV_ARGS(&commandList));
if (FAILED(hr))
{
return false;
}
for (int i = 0; i < frameBufferCount; i++)
{
hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence[i]));
if (FAILED(hr))
{
return false;
}
fenceValue[i] = 0;
}
fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (fenceEvent == nullptr)
{
return false;
}
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ID3DBlob* signature;
hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, nullptr);
if (FAILED(hr))
{
return false;
}
hr = device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&rootSignature));
if (FAILED(hr))
{
return false;
}
ID3DBlob* vertexShader;
ID3DBlob* errorBuff;
hr = D3DCompileFromFile(L"VertexShader.hlsl",
nullptr,
nullptr,
"main",
"vs_5_0",
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
0,
&vertexShader,
&errorBuff);
if (FAILED(hr))
{
OutputDebugStringA((char*)errorBuff->GetBufferPointer());
return false;
}
D3D12_SHADER_BYTECODE vertexShaderBytecode = {};
vertexShaderBytecode.BytecodeLength = vertexShader->GetBufferSize();
vertexShaderBytecode.pShaderBytecode = vertexShader->GetBufferPointer();
ID3DBlob* pixelShader;
hr = D3DCompileFromFile(L"PixelShader.hlsl",
nullptr,
nullptr,
"main",
"ps_5_0",
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
0,
&pixelShader,
&errorBuff);
if (FAILED(hr))
{
OutputDebugStringA((char*)errorBuff->GetBufferPointer());
return false;
}
D3D12_SHADER_BYTECODE pixelShaderBytecode = {};
pixelShaderBytecode.BytecodeLength = pixelShader->GetBufferSize();
pixelShaderBytecode.pShaderBytecode = pixelShader->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC inputLayout[] =
{
{ "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_INPUT_LAYOUT_DESC inputLayoutDesc = {};
inputLayoutDesc.NumElements = sizeof(inputLayout) / sizeof(D3D12_INPUT_ELEMENT_DESC);
inputLayoutDesc.pInputElementDescs = inputLayout;
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.InputLayout = inputLayoutDesc;
psoDesc.pRootSignature = rootSignature;
psoDesc.VS = vertexShaderBytecode;
psoDesc.PS = pixelShaderBytecode;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.SampleDesc = sampleDesc;
psoDesc.SampleMask = 0xffffffff;
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.NumRenderTargets = 1;
psoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
hr = device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineStateObject));
if (FAILED(hr))
{
return false;
}
Vertex vList[] = {
{ -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f },
{ 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f },
{ -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f },
{ 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f },
{ -0.75f, 0.75f, 0.7f, 0.0f, 1.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.7f, 0.0f, 1.0f, 0.0f, 1.0f },
{ -0.75f, 0.0f, 0.7f, 0.0f, 1.0f, 0.0f, 1.0f },
{ 0.0f, 0.75f, 0.7f, 0.0f, 1.0f, 0.0f, 1.0f }
};
int vBufferSize = sizeof(vList);
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vBufferSize),
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&vertexBuffer));
vertexBuffer->SetName(L"Vertex Buffer Resource Heap");
ID3D12Resource* vBufferUploadHeap;
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&vBufferUploadHeap));
vBufferUploadHeap->SetName(L"Vertex Buffer Upload Resource Heap");
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = reinterpret_cast<BYTE*>(vList);
vertexData.RowPitch = vBufferSize;
vertexData.SlicePitch = vBufferSize;
UpdateSubresources(commandList, vertexBuffer, vBufferUploadHeap, 0, 0, 1, &vertexData);
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(vertexBuffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
DWORD iList[] = {
0, 1, 2,
0, 3, 1,
};
int iBufferSize = sizeof(iList);
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(iBufferSize),
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&indexBuffer));
vertexBuffer->SetName(L"Index Buffer Resource Heap");
ID3D12Resource* iBufferUploadHeap;
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&iBufferUploadHeap));
vBufferUploadHeap->SetName(L"Index Buffer Upload Resource Heap");
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = reinterpret_cast<BYTE*>(iList);
indexData.RowPitch = iBufferSize;
indexData.SlicePitch = iBufferSize;
UpdateSubresources(commandList, indexBuffer, iBufferUploadHeap, 0, 0, 1, &indexData);
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(indexBuffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
hr = device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&dsDescriptorHeap));
if (FAILED(hr))
{
Running = false;
}
D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {};
depthStencilDesc.Format = DXGI_FORMAT_D32_FLOAT;
depthStencilDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE;
D3D12_CLEAR_VALUE depthOptimizedClearValue = {};
depthOptimizedClearValue.Format = DXGI_FORMAT_D32_FLOAT;
depthOptimizedClearValue.DepthStencil.Depth = 1.0f;
depthOptimizedClearValue.DepthStencil.Stencil = 0;
device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_D32_FLOAT, Width, Height, 1, 0, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL),
D3D12_RESOURCE_STATE_DEPTH_WRITE,
&depthOptimizedClearValue,
IID_PPV_ARGS(&depthStencilBuffer)
);
hr = device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&dsDescriptorHeap));
if (FAILED(hr))
{
Running = false;
}
dsDescriptorHeap->SetName(L"Depth/Stencil Resource Heap");
device->CreateDepthStencilView(depthStencilBuffer, &depthStencilDesc, dsDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
commandList->Close();
ID3D12CommandList* ppCommandLists[] = { commandList };
commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
fenceValue[frameIndex]++;
hr = commandQueue->Signal(fence[frameIndex], fenceValue[frameIndex]);
if (FAILED(hr))
{
Running = false;
}
vertexBufferView.BufferLocation = vertexBuffer->GetGPUVirtualAddress();
vertexBufferView.StrideInBytes = sizeof(Vertex);
vertexBufferView.SizeInBytes = vBufferSize;
indexBufferView.BufferLocation = indexBuffer->GetGPUVirtualAddress();
indexBufferView.Format = DXGI_FORMAT_R32_UINT;
indexBufferView.SizeInBytes = iBufferSize;
viewport.TopLeftX = 0;
viewport.TopLeftY = 0;
viewport.Width = Width;
viewport.Height = Height;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
scissorRect.left = 0;
scissorRect.top = 0;
scissorRect.right = Width;
scissorRect.bottom = Height;
return true;
}
void Update()
{
}
void UpdatePipeline()
{
HRESULT hr;
WaitForPreviousFrame();
hr = commandAllocator[frameIndex]->Reset();
if (FAILED(hr))
{
Running = false;
}
hr = commandList->Reset(commandAllocator[frameIndex], pipelineStateObject);
if (FAILED(hr))
{
Running = false;
}
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(renderTargets[frameIndex], D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(rtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), frameIndex, rtvDescriptorSize);
CD3DX12_CPU_DESCRIPTOR_HANDLE dsvHandle(dsDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle);
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
commandList->ClearDepthStencilView(dsDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
commandList->SetGraphicsRootSignature(rootSignature);
commandList->RSSetViewports(1, &viewport);
commandList->RSSetScissorRects(1, &scissorRect);
commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->IASetVertexBuffers(0, 1, &vertexBufferView);
commandList->IASetIndexBuffer(&indexBufferView);
commandList->DrawIndexedInstanced(6, 1, 0, 0, 0);
commandList->DrawIndexedInstanced(6, 1, 0, 4, 0);
commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(renderTargets[frameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
hr = commandList->Close();
if (FAILED(hr))
{
Running = false;
}
}
void Render()
{
HRESULT hr;
UpdatePipeline();
ID3D12CommandList* ppCommandLists[] = { commandList };
commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
hr = commandQueue->Signal(fence[frameIndex], fenceValue[frameIndex]);
if (FAILED(hr))
{
Running = false;
}
hr = swapChain->Present(0, 0);
if (FAILED(hr))
{
Running = false;
}
}
void Cleanup()
{
for (int i = 0; i < frameBufferCount; ++i)
{
frameIndex = i;
WaitForPreviousFrame();
}
BOOL fs = false;
if (swapChain->GetFullscreenState(&fs, NULL))
swapChain->SetFullscreenState(false, NULL);
SAFE_RELEASE(device);
SAFE_RELEASE(swapChain);
SAFE_RELEASE(commandQueue);
SAFE_RELEASE(rtvDescriptorHeap);
SAFE_RELEASE(commandList);
for (int i = 0; i < frameBufferCount; ++i)
{
SAFE_RELEASE(renderTargets[i]);
SAFE_RELEASE(commandAllocator[i]);
SAFE_RELEASE(fence[i]);
};
SAFE_RELEASE(pipelineStateObject);
SAFE_RELEASE(rootSignature);
SAFE_RELEASE(vertexBuffer);
SAFE_RELEASE(indexBuffer);
SAFE_RELEASE(depthStencilBuffer);
SAFE_RELEASE(dsDescriptorHeap);
}
void WaitForPreviousFrame()
{
HRESULT hr;
frameIndex = swapChain->GetCurrentBackBufferIndex();
if (fence[frameIndex]->GetCompletedValue() < fenceValue[frameIndex])
{
hr = fence[frameIndex]->SetEventOnCompletion(fenceValue[frameIndex], fenceEvent);
if (FAILED(hr))
{
Running = false;
}
WaitForSingleObject(fenceEvent, INFINITE);
}
fenceValue[frameIndex]++;
}