[Solved][D3D12] Triangle not rendering, vertex data not given to GPU

Started by
5 comments, last by thejelmega 7 years, 9 months ago

I've been following this tutorial: http://www.braynzarsoft.net/viewtutorial/q16390-04-direct3d-12-drawing

But now I've run into an annoying problem, it doesn't render and I've check the graphics debug, like it said in the tutorial and the vertex data isn't given to the gpu. it says that the type is byte2x, but it is a float, and the data it has is 00000000.

If you can help me, thanks.

code:

Renderer.h

[spoiler]


#pragma once
 
#include "Helpers\Util.h"
 
namespace VEH
{
class Window;
}
 
namespace VE
{
class Renderer
{
public:
Renderer(VEH::Window* outputWindow);
~Renderer();
 
bool Init(); //initialize d3d12
void Tick(); // update game logic
void TickPipeline(); // update d3d pipeline (update command list)
void Draw(); // execute command list
void Shutdown(); //release objects and clean up memory
void WaitForPreviousFrame(); // wait until gpu is finished with the command list
 
private:
 
const static int m_FrameBufferCount = 3;  //number of buffers we want, 2 for double buffering, 3 for tripple buffering
 
ID3D12Device*  m_pDevice = nullptr; // direct3d device
IDXGISwapChain3*  m_pSwapChain = nullptr; // swapchain used to switch render targets
ID3D12CommandQueue* m_pCommandQueue = nullptr; // container for command lists
ID3D12DescriptorHeap*  m_pRtvDescriptorHeap = nullptr; // a descriptor heap to hold resources like render targets
ID3D12Resource*  m_paRenderTargets[m_FrameBufferCount] = {}; // number of render targets equal to the buffer count
ID3D12CommandAllocator*  m_paCommandAllocator[m_FrameBufferCount] = {}; // we want enough allocatorsfor each buffer * number of threads (only 1 thread for now)
ID3D12GraphicsCommandList*  m_pCommandList = nullptr; // a command list we can record commands into, the execute them to render the frame
ID3D12Fence*  m_paFence[m_FrameBufferCount] = {}; // an object that is locked while our command list is being executed by the gpu. We need as many as we have allocators (more if we want to know when the gpu is finished with an asset)
ID3D12PipelineState*  m_pPipelineStateObject = nullptr; // pso containing a pipeline state
ID3D12RootSignature* m_pRootSignature = nullptr; // root signature defines data shaders will access
ID3D12Resource* m_pVertexBuffer = nullptr; // a default buffer in DPU memory that we will load vertex data for our triangle into
 
D3D12_VIEWPORT m_Viewport; // area that output from the rasterizer will be stretched to
D3D12_RECT m_ScissorRect; // the area to draw in. pixels outside that area will not be drawn to
D3D12_VERTEX_BUFFER_VIEW m_VertexBufferView; // a structure containing a pointer to the vertex data in gpu memory
// the total size of the buffer, and size of each element (vertex)
 
 
HANDLE m_FenceEvent = NULL; // a handle to an event when out fence is unlocked by the gpu
UINT64 m_FenceValue[m_FrameBufferCount] = {}; // this value is incremented each frame. each fence will have its own value
int m_FrameIndex = 0; // currnet rtv we are on
int m_RtvDescriptorSize = 0; // size of the rtv descriptor on the device (all front and back buffers will be the same size)
 
VEH::Window* m_pWindow;
};
}
 

[/spoiler]

Renderer.cpp

[spoiler]


#include "Renderer.h"
#include "Handlers\Window.h"
 
namespace VE
{
 
Renderer::Renderer(VEH::Window* outputWindow) : m_pWindow(outputWindow)
{
}
 
 
Renderer::~Renderer()
{
}
 
bool Renderer::Init()
{
HRESULT hr;
 
// -- Create Device -- //
 
IDXGIFactory4* dxgiFactory;
hr = (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)));
if (FAILED(hr))
{
return false;
}
 
IDXGIAdapter1* adapter; // adapters are the graphics cards (this includes the embedded graphics on the cpu)
int adapterIndex = 0; // we'll start looking for directx 12 compatible grapics devices starting at index 0
bool adapterFound = false; // set to true when a good one was found
 
while (dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
 
if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)
{
// we don't want a software device
++adapterIndex;
continue;
}
 
//we want a device that is compatible with direct3d 12 (feature level 11 or higher)
hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), nullptr);
if (SUCCEEDED(hr))
{
adapterFound = true;
break;
}
 
++adapterIndex;
}
 
if (!adapterFound)
{
return false;
}
 
//Create the device
hr = D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_pDevice));
if (FAILED(hr))
{
return false;
}
 
// -- Create the Command Queue -- //
 
D3D12_COMMAND_QUEUE_DESC cqDesc = {}; // we'll be using the default values
 
hr = m_pDevice->CreateCommandQueue(&cqDesc, IID_PPV_ARGS(&m_pCommandQueue));
if (FAILED(hr))
{
return false;
}
 
// -- Create the Swap Chain (double/triple buffering)
 
uint32_t windowWidth = m_pWindow->GetWidth();
uint32_t windowHeight = m_pWindow->GetHeight();
 
DXGI_MODE_DESC backBufferDesc = {};
backBufferDesc.Width = windowWidth; // buffer width
backBufferDesc.Height = windowHeight; // buffer height
backBufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // format of the buffer (rgbe 32 bits, 8 bits for each channel)
 
// decribe our multi=sampling. We are not multi-sampling, so we set count to 1 (we need at least 1 sample of course)
DXGI_SAMPLE_DESC sampleDesc = {};
sampleDesc.Count = 1; // multi-sample count (no multi-sampling, so we just put 1, since we still nee 1 sample)
 
// describe and create the swap chain
DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; 
swapChainDesc.BufferCount = m_FrameBufferCount; // number of buffers we have
swapChainDesc.BufferDesc = backBufferDesc; // our back buffer description
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // this says the pipeline will render to this swap chain
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // dxgi will discrad the buffer (data) after we call present
swapChainDesc.OutputWindow = m_pWindow->GetHwnd(); //handle to our window
swapChainDesc.SampleDesc = sampleDesc; // our multi-sampling description
swapChainDesc.Windowed = !m_pWindow->IsFullscreen(); // set to true, then if in fullscreen must call SetFullScreenState with true for full screen to get uncapped fps
 
IDXGISwapChain* tempSwapChain;
 
dxgiFactory->CreateSwapChain(
m_pCommandQueue, // the queue will be flushed once the swap chain is created
&swapChainDesc,  // give it the swapchain description we created above
&tempSwapChain); // store the created swap cahin in a temp IDXGSwapChain Interface
 
m_pSwapChain = static_cast<IDXGISwapChain3*>(tempSwapChain);
 
m_FrameIndex = m_pSwapChain->GetCurrentBackBufferIndex();
 
// -- Create the Back Buffers (render target views) Descriptor heap -- //
 
// describe a rtv descriptor heap and creat it
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = m_FrameBufferCount; // number of descriptors for this heap
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
 
// this heap will not be directly referenced by the shaders (not shader visible), as this will store the output from the pipeline
// otherwixe we would set the heap's flag to D3D12_DESCRIPTION_HEAP_FLAG_SHADER_VISIBLE
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
hr = m_pDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&m_pRtvDescriptorHeap));
if (FAILED(hr))
{
return false;
}
 
// get the size of a descriptor in this heap (this is a rtv hea[, so only rtv descriptors should be stored in it.
// descriptor sixes may vary from device to device, which is why there is no set size and we must ask the
// device to give us the size. we will use this size to increment a descriptor handle offset
m_RtvDescriptorSize = m_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
 
// get a handle to the first descriptor in the descriptor heap. ahnadle is basically a pointer,
// but we cannot literally use it like a c++ pointer
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_pRtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
 
//Create a RTV for each buffer (double buffering is 2 buffers, triple buffering is 3)
for (int i = 0; i < m_FrameBufferCount; ++i)
{
// first we get the n'th buffer in the swap chain and store it in the n'th
// position of our ID3D12Resource array
hr = m_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&m_paRenderTargets[i]));
if (FAILED(hr))
{
return false;
}
 
// then we "create: a render target view which binds the swap chain buffer (ID3D12Resource[n]) to the rtv handle
m_pDevice->CreateRenderTargetView(m_paRenderTargets[i], nullptr, rtvHandle);
 
//we increment the rv handle by the rtv desriptor size we got above
rtvHandle.Offset(1, m_RtvDescriptorSize);
}
 
// -- Create the Command Allocators -- //
 
for (int i = 0; i < m_FrameBufferCount; ++i)
{
hr = m_pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_paCommandAllocator[i]));
if (FAILED(hr))
{
return false;
}
}
 
// create the command list with the first allocator
hr = m_pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_paCommandAllocator[0], NULL, IID_PPV_ARGS(&m_pCommandList));
if (FAILED(hr))
{
return false;
}
 
// command lists are created in the recording state. our main loop will set it up for recording afain, so close it now
m_pCommandList->Close();
 
// -- Create a Fence & Fence Event -- //
 
// create the fences
for (int i = 0; i < m_FrameBufferCount; ++i)
{
hr = m_pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_paFence[i]));
if (FAILED(hr))
{
return false;
}
m_FenceValue[i] = 0; // set the initial fence value to 0
}
 
//create a handle to a fence event
m_FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_FenceEvent == nullptr)
{
return false;
}
 
// create root signature
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 = m_pDevice->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_pRootSignature));
if (FAILED(hr))
{
return false;
}
 
// create vertex and pixel shader
 
// when debugging, we can compilre the shader files at runtime.
// but for release versions, we can compile the hlsl shaders
// with fxc.wxw to create .cso files, which contain the shader
// bytecode. We can load the .cso files at runtime to get the
// shader bytecode, shich of course is faster than compiling
// them at runtime
 
//compile vertex shader
ID3DBlob* vertexShader; // d3d blob for holding vertex shader byte code
ID3DBlob* errorBuff; // a buffer holding the error data if any
hr = D3DCompileFromFile(L"Resources/Shaders/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;
}
 
//fill out a shader bytecode structure, which is basically just a pointer
// to the shader bytecode and the size of the shader bytecode
D3D12_SHADER_BYTECODE vertexShaderBytecode = {};
vertexShaderBytecode.BytecodeLength = vertexShader->GetBufferSize();
vertexShaderBytecode.pShaderBytecode = vertexShader->GetBufferPointer();
 
//compile pixel shader
ID3DBlob* pixelShader; // a buffer holding the error data if any
hr = D3DCompileFromFile(L"Resources/Shaders/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;
}
 
// fil out bytecode for the pixel shader
D3D12_SHADER_BYTECODE pixelShaderBytecode = {};
pixelShaderBytecode.BytecodeLength = pixelShader->GetBufferSize();
pixelShaderBytecode.pShaderBytecode = pixelShader->GetBufferPointer();
 
// create the input layout
 
// the input layout is used by the Input Assembler so that it knows
// how to read the vertex sata bound to it
 
D3D12_INPUT_ELEMENT_DESC inputLayout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
 
//fill out an input layout description
D3D12_INPUT_LAYOUT_DESC inputLayoutDesc = {};
 
//we can get the number of elements in an array by "sizeof(array) / sizeof(arrayElementType)"
inputLayoutDesc.NumElements = sizeof(inputLayout) / sizeof(D3D12_INPUT_ELEMENT_DESC);
inputLayoutDesc.pInputElementDescs = inputLayout;
 
// create a Pipeline State Object (PSO)
 
// In a real application, you will have many pso's. for each different shader
// or different combinations of shaders, different blend states or different rasterizer states,
// different topology types (point, line, triangle, patch), or a different number
// of render targets you will need a pso
 
// VS is the only required shader for a pso. You might be wondering when a case would be where
// you only set the VS. It's possible that you have a pso that only outputs data with the stream
// output, and not on a render target, which means you would not need anything after the stream
// output.
 
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; // a structure to define a pso
psoDesc.InputLayout = inputLayoutDesc; // the structure describing our input layout
psoDesc.pRootSignature = m_pRootSignature; // the root signature thath describes the input data this pso needs
psoDesc.VS = vertexShaderBytecode; // structure describing where to find the vertex shader bytecode and how large it is
psoDesc.PS = pixelShaderBytecode; // same as VS, but for pixel shader
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // type of topology we are drawing
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; // format of the render target
psoDesc.SampleDesc = sampleDesc; // must be the same sample description as the swapchain and depth/stencil buffer
psoDesc.SampleMask = 0xffffffff; // sample mask has to do with multi-sampling. 0xffffffff means point sampling is done
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); // a default rasterizer state
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); // a default belnd state
psoDesc.NumRenderTargets = 1; // we are only binding one render target
 
//create the pso
hr = m_pDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pPipelineStateObject));
if (FAILED(hr))
{
return false;
}
 
// Create vertex buffer
 
// a triangle
Vertex vList[] = {
{ {0.0f, 0.5f, 0.5f} },
{ {0.5f, -0.5f, 0.5f} },
{ {-0.5f, -0.5f, 0.5f} }
};
 
uint32_t vBufferSize = sizeof(vList);
 
// create default heap
// deault heao is memory on the gpu. Only the GPU has acces to this memery
// To get data into this heap, we will have to upload the data using
// an upload heap
m_pDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), // a default heap
D3D12_HEAP_FLAG_NONE, // no flags
&CD3DX12_RESOURCE_DESC::Buffer(vBufferSize), // resource description for a buffer
D3D12_RESOURCE_STATE_COPY_DEST, // we will start the heap in the copy destination state since we will copy data
// from the upload heap to this
nullptr, // optimize clear value must be null for this type of resouurce, used for render targets ans depth/stencil buffers
IID_PPV_ARGS(&m_pVertexBuffer));
 
// we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at
m_pVertexBuffer->SetName(L"Vertex Buffer Resource Heap");
 
// create upload heap
// upload heaps are used to upload data to the GPU. CPU can wrtie to it, GPU can read from it
// we will upload the vertex buffer using this heap to te default heap
ID3D12Resource* vBufferUploadHeap;
 
m_pDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), //upload heap
D3D12_HEAP_FLAG_NONE, // no flags
&CD3DX12_RESOURCE_DESC::Buffer(vBufferSize), //read description for a buffer
D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap
nullptr,
IID_PPV_ARGS(&vBufferUploadHeap));
vBufferUploadHeap->SetName(L"Vertex Buffer Upload Resource Heap");
 
//store vertex buffer in upload heap
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = reinterpret_cast<BYTE*>(vList); //pointer to our vertex data
vertexData.RowPitch = vBufferSize; // size of all our triangle vertex data
vertexData.SlicePitch = vBufferSize; // als the size of our triangle vertex data
 
// we are now creating a command with the comand list to copy the data from
// the upload heap to the default heap
UpdateSubresources(m_pCommandList, m_pVertexBuffer, vBufferUploadHeap, 0, 0, 1, &vertexData);
 
// transition the vertex buffer data from copy destination to vertex buffer state
m_pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_pVertexBuffer, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
 
//Now we execute the command list to upload the initial assets (triangle data
m_pCommandList->Close();
ID3D12CommandList* ppCommandLists[] = { m_pCommandList };
m_pCommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists);
 
//increment the fence value now, otherwise the buffer might not be uploaded by the time we start drawing
++m_FenceValue[m_FrameIndex];
hr = m_pCommandQueue->Signal(m_paFence[m_FrameIndex], m_FenceValue[m_FrameIndex]);
if (FAILED(hr))
{
m_pWindow->Close();
}
 
// create a vertex buffer view for the triangle, We get the GPU memory address to the vertex pointer using the GetGpuVirtualAddress() method
m_VertexBufferView.BufferLocation = m_pVertexBuffer->GetGPUVirtualAddress();
m_VertexBufferView.StrideInBytes = sizeof(Vertex);
m_VertexBufferView.SizeInBytes = vBufferSize;
 
// fill out the viewport
m_Viewport.TopLeftX = 0;
m_Viewport.TopLeftY = 0;
m_Viewport.Width = windowWidth;
m_Viewport.Height = windowHeight;
m_Viewport.MinDepth = 0.0f;
m_Viewport.MaxDepth = 1.0f;
 
// fill out a scissor rect
m_ScissorRect.left = 0;
m_ScissorRect.top = 0;
m_ScissorRect.right = windowWidth;
m_ScissorRect.bottom = windowHeight;
 
return true;
}
 
void Renderer::Tick()
{
// update app logic, such as moving the camera or figuring out what objects are in view (for now)
}
 
void Renderer::TickPipeline()
{
HRESULT hr;
 
// we have to wiat for the gpu to finish with the command allocator before we can reset it
WaitForPreviousFrame();
 
// we can only reset the allocator once the gpu is done with it
// resetting an allocator frees the memory that the command list was stored in
hr = m_paCommandAllocator[m_FrameIndex]->Reset();
if (FAILED(hr))
{
m_pWindow->Close();
}
 
// reset the command list. by resetting the command list we are putting it into
// a recording state so we can start recording commands into the command allocator.
// the command allocator that we reference here may have multiple command lists
// associated with it, but only one can be in the recording state at any time. Make sure
// that any other command lists associated with this command allocator are in
// the xlosed state (not recording).
// Here you will pass the initiali pipeline state object as the second parameter,
// but for now we are only clearing the rtc, and do not actually need
// anything but an initial default pipeline, which is what we get by setting
// the second parameter to NULL
hr = m_pCommandList->Reset(m_paCommandAllocator[m_FrameIndex], m_pPipelineStateObject);
if (FAILED(hr))
{
m_pWindow->Close();
}
 
// here we start recording commands into the command list (all the command will be stored in the command allocator)
 
// transition the "frame index" render target from the present state to  render target state so the command list draws to it starting from here
m_pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_paRenderTargets[m_FrameIndex], D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));
 
// here we again get the handle to our current render target wiew so we can set it as the render target in the output merger stage of the pipeline
CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_pRtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), m_FrameIndex, m_RtvDescriptorSize);
 
// set the render target for the output merger stage (the output of the pipleine)
m_pCommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
 
// Clear the render target by using the ClearRenderTargetView command
const float clearColor[] = {0.0f, 0.2f, 0.4f, 1.0f};
m_pCommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);
 
//draw triangle
m_pCommandList->SetGraphicsRootSignature(m_pRootSignature); // set the root signature
m_pCommandList->RSSetViewports(1, &m_Viewport); // set the viewports
m_pCommandList->RSSetScissorRects(1, &m_ScissorRect); // set the scissor rects
m_pCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); //set the primitive topology
m_pCommandList->IASetVertexBuffers(0, 1, &m_VertexBufferView); // set the vertex buffer (using the vertex buffer view)
m_pCommandList->DrawInstanced(3, 1, 0, 0); // finaly draw 3 vertices (draw the triangle)
 
// transition the "frame index" render target from the render target state to the present state. If the debug layer is enable , you will recieve a
// warning if present is called on the render target when it's not in the present state
m_pCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_paRenderTargets[m_FrameIndex], D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));
 
hr = m_pCommandList->Close();
if (FAILED(hr))
{
m_pWindow->Close();
}
}
 
void Renderer::Draw()
{
HRESULT hr;
 
TickPipeline(); // update the pipeline by sending commands to the command queue
 
// create an array of command lists (only one command lis here)
ID3D12CommandList* ppCommandList[] = { m_pCommandList };
 
//execute the array of command lists
m_pCommandQueue->ExecuteCommandLists(_countof(ppCommandList), ppCommandList);
 
// This command goes in at the end of our command queue. we will know our command queue
// has finished because the fence value will be set to "fenceValue" from the GPU since the command 
// queue is being executed on the GPU
hr = m_pCommandQueue->Signal(m_paFence[m_FrameIndex], m_FenceValue[m_FrameIndex]);
if (FAILED(hr))
{
m_pWindow->Close();
}
 
//present the current backbuffer
hr = m_pSwapChain->Present(0, 0);
if (FAILED(hr))
{
m_pWindow->Close();
}
}
 
void Renderer::Shutdown()
{
CloseHandle(m_FenceEvent);
 
//wait for the gpu to finish all frames
for (int i = 0; i < m_FrameBufferCount; ++i)
{
m_FrameIndex = i;
WaitForPreviousFrame();
}
 
//get swapchain out of full screen before exiting
BOOL fs = false;
if (m_pSwapChain->GetFullscreenState(&fs, NULL))
m_pSwapChain->SetFullscreenState(false, NULL);
 
SAFE_RELEASE(m_pDevice);
SAFE_RELEASE(m_pSwapChain);
SAFE_RELEASE(m_pCommandQueue);
SAFE_RELEASE(m_pRtvDescriptorHeap);
SAFE_RELEASE(m_pCommandList);
 
for (int i = 0; i < m_FrameBufferCount; ++i)
{
SAFE_RELEASE(m_paRenderTargets[i]);
SAFE_RELEASE(m_paCommandAllocator[i]);
SAFE_RELEASE(m_paFence[i]);
}
 
SAFE_RELEASE(m_pPipelineStateObject);
SAFE_RELEASE(m_pRootSignature);
SAFE_RELEASE(m_pVertexBuffer);
}
 
void Renderer::WaitForPreviousFrame()
{
HRESULT hr;
 
// swap the surrent rtv buffer so we can draw on the correct buffer
m_FrameIndex = m_pSwapChain->GetCurrentBackBufferIndex();
 
// if command queue fence value is still less than "fenceValue", then we know the GPU has not finished executing
// the command queue since it has not reached the "CommandQueue->Signal(fence, fenceValue)" command
if (m_paFence[m_FrameIndex]->GetCompletedValue() < m_FenceValue[m_FrameIndex])
{
// we have the fence create an event which is signaled once the fence's current value is "fenceValue"
hr = m_paFence[m_FrameIndex]->SetEventOnCompletion(m_FenceValue[m_FrameIndex], m_FenceEvent);
if (FAILED(hr))
{
m_pWindow->Close();
}
 
// we will wait until the fence has triggered the even that it's current value has reached "fenceValue". once its value
// has reached "fenceValue", we know the command queue has finished executing
WaitForSingleObject(m_FenceEvent, INFINITE);
}
 
//increment fenceValue for next frame
++m_FenceValue[m_FrameIndex];
}
 
}

[/spoiler]

Util.h

[spoiler]


#pragma once
 
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely used stuff from the windows header
#endif
 
#include <Windows.h>
#include <d3d12.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>
#include "d3dx12.h"
 
#include <iostream>
#include <vector>
#include <string>
#include <assert.h>
 
#define SAFE_RELEASE(p) { if ( (p) ) { (p)->Release(); (p) = 0; } }
#define SAFE_DELETE(a) if( (a) != NULL ) delete (a); (a) = NULL;
 
struct Vertex
{
DirectX::XMFLOAT3 pos;
};

[/spoiler]

VertexShader.hlsl

[spoiler]


float4 main(float3 pos : POSITION) : SV_POSITION
{
// just pass vertex position straight through
return float4(pos, 1.0f);
}

[/spoiler]

VertexShader.hlsl

[spoiler]


float4 main() : SV_TARGET
{
//return green
return float4(0.0f, 1.0f, 0.0f, 1.0f);
}

[/spoiler]

Advertisement

The first thing you should do is enable the debug layer before creating your device. The debug layer will output helpful messages when you use the API incorrectly. You can enable it like this:

ID3D12Debug* d3d12debug = nullptr;
if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&d3d12debug))))
{
    d3d12debug->EnableDebugLayer();
    d3d12debug->Release();
    d3d12debug = nullptr;
}

OK, I've added that code and I get this:

D3D12 ERROR: ID3D12GraphicsCommandList::*: This API cannot be called on a closed command list. [ EXECUTION ERROR #547: COMMAND_LIST_CLOSED]

Because I'm new when it comes to graphics programming, I'm not sure where my problem is.

EDIT: after looking at the the tutorial code once again, I found the problem, in the tutorial source code, a close call was removed, but they did not tell that in the tutorial, but I'm extremely thankful for the help, MJP.

Especially because after the vacation, my second year game development will start and then we will only start with graphics programming, so I wanted to be a bit ahead.

Honestly, if you are new to graphics programming then I would recommend NOT using D3D12 (or Vulkan).

They basically switch everything to hard mode and force you to deal with a lot of things that you really don't want to be dealing with when new.

DX11 (and to a slightly lesser extent 4.3+ GL) are much better starting platforms for getting the hang of how to do this stuff.

I've actually already programmed with opengl, it's just that I didn't think of when I was typing that, because it has already been a few months since I did that.

And the reason I chose to learn some D3D12, is because I like to make it challenging to myself, also, I have lot of free time now, so I'm taking it slow, school only starts in the last week of september.

I've actually already programmed with opengl, it's just that I didn't think of when I was typing that, because it has already been a few months since I did that.

And the reason I chose to learn some D3D12, is because I like to make it challenging to myself, also, I have lot of free time now, so I'm taking it slow, school only starts in the last week of september.

Seriously, take phantom's advice. Start with DX11 and then move to 12 once you're completely comfortable with it. DX12 absolutely expects you to be very experienced with DX11, and then cranks up the difficulty dial ridiculously high. It's not a beginner subject.

I understand wanting a challenge, but there's just so many absolutely unforgiving details about 12 which you just can't know going into this blind. Just look at the amount of similar threads created here from people who have trouble getting the most basic elemental things like a triangle or rectangle on screen. It's completely insane.

Please, do yourself a favor, start with DX11 so you don't have to deal with so many ridiculously complex moving parts which you really don't want to deal with if all you want to do is doing actual graphics work. At this point you're not going to be able to beat the driver in terms of efficiency, which is what DX12 is all about. Even more so, you're going to do way worse than what the driver can do in DX11, leading to horrible performance. This is not a thing about "if I try hard enough I'll succeed", you're really not going to get anything out of DX12 at this point.

I gets all your texture budgets!

Ok, I'll be sure to do that.

I've looked on my computer that I once started with dx11, but never really continued, so I'm going to do that and if I have something nice, I can always port it later. I'll start with dx11 and than I'm also going to have a head start for next school year.

Thanks for the info.

This topic is closed to new replies.

Advertisement