I am trying to use DX12 to draw a Triangle in a Window with Visual C++.
For me everything seems to be in place required for it, and it is all working fine too, all DX12 Objects are created successfully, the Render Targets are cleared with the provided clear color, but the Triangle is not visible on the screen.
The weird thing about asking for help for it is that i get no errors besides that it is not doing what it should... why the only thing i can really do to give a picture about my issue, is to provide the entire project, since i have no idea what the problem is, because i get no Compile, Runtime, or DX12 Erros (I have the Debug Layer Enabled with GPU Based Validation), and when checking everything with breakpoints step by step, nothing seemed wrong too.
Here a little overview and remarks about my VS-Project for better orientation
I have a singleton class called Process which holds all the functionality
The Process Execution is as followed:
The Render() Function is executed when the WM_PAINT is Dispatched on the WinProc in order to record and execute Command Lists on the Command Queue ...
I am using the D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT Flag on my RootSignature, in order to input the Vertex Buffer to the PSO
Edit: Here the Minimal Reproducible Example Version of the Process::InitalizeProcess() and Process::Render() Functions
int Process::InitalizeProcess(HINSTANCE hInstance, LPSTR lpCmdLine, int nCmdShow)
{
//Window Creation Excluded for simplicity
//Device & Adapter Creation Excluded for simplicity
// Create Command Queue
//Command Queue Description
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
ThrowIfFailed(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)),
"Failed to create Command Queue")
// Create Command Allocator
ThrowIfFailed(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)),
"Failed to create Command Allocator")
// Create Swapchain
//Swapchain Description
DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
SwapChainDesc.BufferCount = 2;
SwapChainDesc.BufferDesc.Width = 900;
SwapChainDesc.BufferDesc.Height = 600;
SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
SwapChainDesc.OutputWindow = hWnd; //Important !!! Specifies the output Window
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.Windowed = TRUE;
//Create Swapchain
ThrowIfFailed(factory->CreateSwapChain(commandQueue, &SwapChainDesc, (IDXGISwapChain**)&swapchain),
"Failed to create Swapchain");
//Query Interface
ThrowIfFailed(swapchain->QueryInterface(IID_PPV_ARGS(&swapchain)),
"Failed to Query Interface to Swapchain4");
// Create RTV Descriptor Heap
D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
rtvHeapDesc.NodeMask = 0;
rtvHeapDesc.NumDescriptors = 2;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
ThrowIfFailed(device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap)),
"Failed to Create RTV Descriptor Heap");
//Populate RTV Descriptor Heap
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle = rtvHeap->GetCPUDescriptorHandleForHeapStart();
for (int n = 0; n < 2; n++)
{
ThrowIfFailed(swapchain->GetBuffer(n, IID_PPV_ARGS(&rtBuffers[n])),
"Swapchain::GetBuffer Failed!");
device->CreateRenderTargetView(rtBuffers[n], nullptr, cpu_handle);
//Increment cpu_handle
cpu_handle.ptr += device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
}
// Shader Compliation
ID3DBlob* vertexShader;
ID3DBlob* pixelShader;
#if defined (_DEBUG)
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = NULL;
#endif
//VertexShader
ThrowIfFailed(D3DCompileFromFile(L"VertexShader.hlsl", nullptr, nullptr, "main", "vs_5_0", compileFlags, 0, &vertexShader, 0),
"Failed to Compile PixelShader");
//Pixel Shader
ThrowIfFailed(D3DCompileFromFile(L"PixelShader.hlsl", nullptr, nullptr, "main", "ps_5_0", compileFlags, 0, &pixelShader, 0),
"Failed to compile Pixel Shader");
// Create Vertex Buffer
float VB_Data[] =
{
0.0f , 0.25f,
0.25f , -0.25f,
-0.25f , -0.25f
};
//Heap Properties
D3D12_HEAP_PROPERTIES RessourceProperties = {};
RessourceProperties.Type = D3D12_HEAP_TYPE_UPLOAD;
RessourceProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
RessourceProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
RessourceProperties.CreationNodeMask = 0;
RessourceProperties.VisibleNodeMask = 0;
//Resource Description
D3D12_RESOURCE_DESC RessourceDesc = {};
RessourceDesc.Format = DXGI_FORMAT_UNKNOWN;
RessourceDesc.Alignment = 0;
RessourceDesc.MipLevels = 1;
RessourceDesc.DepthOrArraySize = 1;
RessourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
RessourceDesc.Height = 1;
RessourceDesc.Width = sizeof(VB_Data);
RessourceDesc.SampleDesc.Count = 1;
RessourceDesc.SampleDesc.Quality = 0;
RessourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
RessourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
//Create Upload Heap
ThrowIfFailed(device->CreateCommittedResource(
&RessourceProperties,
D3D12_HEAP_FLAG_NONE,
&RessourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&VertexBuffer)),
"Failed to Create CommittedResource")
//Map
float* pSharedData;
ThrowIfFailed(VertexBuffer->Map(0, nullptr, (void**)&pSharedData),
"Failed to Map Vertex Buffer");
//Copy
memcpy(pSharedData, VB_Data, sizeof(VB_Data));
//Unmap
VertexBuffer->Unmap(0, nullptr);
//Define View
VertexBufferView.BufferLocation = VertexBuffer->GetGPUVirtualAddress();
VertexBufferView.SizeInBytes = sizeof(VB_Data);
VertexBufferView.StrideInBytes = sizeof(float) * 2;
// Create Root Signature
ID3DBlob* RootBlob;
D3D12_ROOT_SIGNATURE_DESC Root_Desc = {};
Root_Desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
Root_Desc.NumStaticSamplers = 0;
Root_Desc.NumParameters = 0;
//Serialize Root Signature
ThrowIfFailed(D3D12SerializeRootSignature(&Root_Desc, D3D_ROOT_SIGNATURE_VERSION_1, &RootBlob, nullptr),
"Failed to Serialize Root Signature")
//Create Root Signature
ThrowIfFailed(device->CreateRootSignature(0, RootBlob->GetBufferPointer(), RootBlob->GetBufferSize(), IID_PPV_ARGS(&RootSignature)),
"Failed to Create Root Signature")
// Create Pipeline State Object
//Input Layout
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
};
//PSO Description
D3D12_GRAPHICS_PIPELINE_STATE_DESC PSO_Desc = {};
PSO_Desc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
PSO_Desc.pRootSignature = RootSignature;
PSO_Desc.VS.BytecodeLength = vertexShader->GetBufferSize();
PSO_Desc.VS.pShaderBytecode = vertexShader->GetBufferPointer();
PSO_Desc.PS.BytecodeLength = pixelShader->GetBufferSize();
PSO_Desc.PS.pShaderBytecode = pixelShader->GetBufferPointer();
PSO_Desc.NodeMask = 0;
PSO_Desc.SampleDesc.Count = 1;
PSO_Desc.SampleDesc.Quality = 0;
PSO_Desc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
PSO_Desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
PSO_Desc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
PSO_Desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
PSO_Desc.NumRenderTargets = 1;
PSO_Desc.RTVFormats[0] = SwapChainDesc.BufferDesc.Format;
//Create PSO
ThrowIfFailed(device->CreateGraphicsPipelineState(&PSO_Desc, IID_PPV_ARGS(&PSO)),
"Failed to Create PSO");
//Create Fence
ThrowIfFailed(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&Fence)),
"Failed to Create Fence");
//Create Command List
ThrowIfFailed(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, PSO, IID_PPV_ARGS(&commandList)),
"Failed to create Command List")
commandList->Close();
}
void Process::Render()
{
if (swapchain)
{
//Reset Command List and Allocator
commandAllocator->Reset();
commandList->Reset(commandAllocator, PSO);
//Set Viewport and Viewport Rect
D3D12_VIEWPORT Viewport = {};
D3D12_RECT ViewRect = {};
GetWindowRect(hWnd, &ViewRect);
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.MinDepth = 0;
Viewport.MaxDepth = 1;
Viewport.Height = ViewRect.bottom - ViewRect.top;
Viewport.Width = ViewRect.right - ViewRect.left;
commandList->RSSetViewports(1, &Viewport);
commandList->RSSetScissorRects(1, &ViewRect);
//Set Root Signature
commandList->SetGraphicsRootSignature(RootSignature);
//Indicate that Backbuffer will be used as Render Target
D3D12_RESOURCE_BARRIER rb1 = {};
rb1.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
rb1.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
rb1.Transition.pResource = rtBuffers[swapchain->GetCurrentBackBufferIndex()];
rb1.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
rb1.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
rb1.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
commandList->ResourceBarrier(1, &rb1);
//Set RenderTargets
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle = rtvHeap->GetCPUDescriptorHandleForHeapStart();
cpu_handle.ptr += swapchain->GetCurrentBackBufferIndex() * device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
commandList->OMSetRenderTargets(1, &cpu_handle, false, nullptr);
//Clear Render Target View
const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f };
commandList->ClearRenderTargetView(cpu_handle, clearColor, 0, nullptr);
//Draw Triangle
commandList->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->IASetVertexBuffers(0, 1, &VertexBufferView);
commandList->DrawInstanced(3, 1, 0, 0);
//Indicate that Backbuffer will now be used to Present
D3D12_RESOURCE_BARRIER rb2 = {};
rb2.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
rb2.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
rb2.Transition.pResource = rtBuffers[swapchain->GetCurrentBackBufferIndex()];
rb2.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
rb2.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
rb2.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
commandList->ResourceBarrier(1, &rb2);
//Execute Command Lists
commandList->Close();
commandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&commandList);
//Wait For Command Queue
Fence->Signal(0);
commandQueue->Signal(Fence, 1);
while (Fence->GetCompletedValue() == 0)
{
}
//Present
swapchain->Present(1, 0);
}
}
Vertex Shader:
float4 main( float2 pos : POSITION ) : SV_POSITION
{
return float4(pos, 0.0f, 0.0f);
}
Pixel Shader:
float4 main() : SV_TARGET
{
return float4(0.0f, 1.0f, 0.0f, 1.0f);
}
The problem was that the BlendState
in the Pipeline
State Object was not defined(but initialized). After doing so the triangle was visible.