Prerequisite and source location#
Configure the samples in setup. Open samples/dx12/main.cpp and follow Sample::Init, CreateTargets, ResizeIfNeeded, and Render. This chapter explains the objects already used by both complete sample targets; it does not introduce a separate incompatible window wrapper. A device creates GPU objects. A command queue executes work. A swap chain owns presentation buffers associated with a window. A render-target view describes a buffer as a color attachment. Their creation order follows those dependencies.
Create the device before its resources#
Enable the debug layer before calling D3D12CreateDevice. Create an IDXGIFactory6 and enumerate adapters by high-performance preference. Skip software adapters unless --warp was requested. Test feature-level support by creating the device, then query Shader Model 6.0 support because the offline shader targets need it. The sample’s feature level is not a promise that ray tracing, mesh shaders, or every advanced format is available. A production backend queries each required feature and chooses a supported technique or reports a clear requirement.
D3D12_COMMAND_QUEUE_DESC queueDesc{};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
Check(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&queue)));Check is the HRESULT helper defined in main.cpp. Sample retains the device and queue through ComPtr. Destroying a CPU reference while work is in flight is still unsafe unless another owner retains the object until completion.
Create presentation targets#
The sample uses a two-buffer flip-discard swap chain with R8G8B8A8_UNORM storage. It obtains IDXGISwapChain3 and queries GetCurrentBackBufferIndex before recording each frame. An RTV heap reserves one descriptor per buffer; each CPU handle advances by the device-reported descriptor increment. The RTV uses the sRGB view format so linear shader output is encoded for an SDR display. The textured path adds a D32_FLOAT depth attachment and a DSV. The color attachment format in the pipeline must match the view used for rendering.
Record a clear and a draw#
Transition the current back buffer from PRESENT to RENDER_TARGET. Bind its RTV, clear it, set viewport and scissor, bind the pipeline and root signature, and record the indexed draw. Transition back to PRESENT, close the command list, execute it, and call Present(1,0). Present submits presentation work; it does not mean every resource is now safe to recycle. Signal a fence on the queue and store that completion value in the frame context. Before that context’s allocator is reused, wait for its prior value.
Resize without stale targets#
A WM_SIZE callback records requested dimensions. The render loop ignores zero dimensions while minimized. For a real resize, it waits for queued GPU work, releases references to old back buffers and depth, calls ResizeBuffers, and recreates RTVs and the depth resource. Viewport, scissor, and camera aspect ratio use the new dimensions. Releasing only the swap-chain pointer is insufficient if a command or another ComPtr still references an old target. More advanced renderers retire offscreen attachments by fence instead of flushing the whole queue, but a teaching resize can intentionally flush.
Build, inspect, diagnose#
Run the build commands from setup, then launch either target. The triangle chapter explains the pipeline that makes the visible geometry. Test resizing while observing debug output. An INVALID_RESOURCE_STATE message points to barriers; an allocator reset error points to completion; ResizeBuffers failure often points to retained target references. A black window does not prove device creation failed. Check whether the loop renders, the queue executes, the target is bound, the viewport is nonzero, and shaders/pipeline are valid. Microsoft’s initialization flow is a useful official companion.