DirectX Tool Kit for DirectX 12

repository·main·Indexed 23 days ago

https://github.com/microsoft/directxtk12

A collection of helper classes designed to simplify writing Direct3D 12 C++ code for Windows 10/11, Xbox Series X|S, Xbox One, and UWP applications. It provides modules for audio (XAudio2), graphics (textures, primitives, models, 2D/UI), input (GamePad, Keyboard, Mouse), and math (SimpleMath wrapper for DirectXMath).

Tokens
14.5K
Snippets
28
Records
72
Agent score
82%

What's inside DirectXTK12

  1. Core concepts for DirectX 12 resource management

    main

    DirectXTK12 provides abstractions to handle the complexities of DirectX 12:

    • Pipeline State Objects (PSOs): Use EffectPipelineStateDescription and RenderTargetState to configure PSOs for effects and rendering helpers.
    • Descriptor Heaps: Use DescriptorHeap to manage shader-visible descriptor heaps. Rendering classes typically require descriptor heap indices at draw time.
    • Resource Upload: Use ResourceUploadBatch to batch GPU resource uploads into a single command list for efficiency.
    • Graphics Memory: GraphicsMemory manages per-frame dynamic allocations (like constant buffers or dynamic vertex/index buffers). You must call GraphicsMemory::Commit once per frame after executing command lists.
  2. How BasicEffect manages Pipeline State Objects (PSOs)

    main

    In Direct3D 12, BasicEffect encapsulates the monolithic Pipeline State Object (PSO). Unlike Direct3D 11, where states could be changed independently, a PSO in Direct3D 12 combines the shader programs, root signature, and fixed-function states into a single object that cannot be changed after creation.

    To create a BasicEffect, you must provide an EffectPipelineStateDescription which includes:

    • Input layout: Derived from your vertex type (e.g., VertexType::InputLayout).
    • Blend state: e.g., CommonStates::Opaque.
    • Depth/stencil state: e.g., CommonStates::DepthNone.
    • Rasterizer state: e.g., CommonStates::CullNone.
    • Render target state: The format of the render target and depth buffer.

    If you need to change rendering states (like switching from opaque to alpha blending), you must create a new BasicEffect instance with a different PSO. Calling m_effect->Apply(commandList) sets the PSO, root signature, and uploads the constant buffers (world/view/projection matrices) to the GPU.

  3. Understand dependency management with vcpkg

    main

    This project uses vcpkg manifest mode for dependency management:

    • vcpkg.json: Defines the required libraries.
    • vcpkg-configuration.json: Provides a specific commit ID for the vcpkg registry to control dependency versions.

    MSBuild Integration

    The .vcxproj file enables manifest mode via <VcpkgEnableManifest>true</VcpkgEnableManifest>. It also imports vcpkg props and targets from the Visual Studio installation directory. This requires the Microsoft.VisualStudio.Component.Vcpkg component to be installed in Visual Studio 2022 or later.

    CMake Integration

    CMakePresets.json configures CMAKE_TOOLCHAIN_FILE using the vcpkg toolchain discovered via the VCPKG_ROOT environment variable. This allows find_package() to locate the installed packages automatically.

  4. Handle bitmask flags using typed enums

    main

    DirectXTK12 uses typed enum bitmask flags. This means you cannot pass a 0 literal as a flags value. You must use the appropriate default enum value.

    Correct usage pattern:

    WIC_LOADER_FLAGS flags = WIC_LOADER_DEFAULT;
    if (condition) {
        flags |= WIC_LOADER_FORCE_SRGB;
    }

    Required default values for common loaders:

    • AudioEngine_Default
    • SoundEffectInstance_Default
    • ModelLoader_Clockwise
    • DDS_LOADER_DEFAULT
    • WIC_LOADER_DEFAULT
    WIC_LOADER_FLAGS flags = WIC_LOADER_DEFAULT;
    if (...) flags |= WIC_LOADER_FORCE_SRGB;
  5. How ResourceUploadBatch manages texture uploads

    main

    A ResourceUploadBatch coordinates the multi-step process of moving texture data from the CPU to the GPU:

    1. Begin(): Starts recording the upload commands.
    2. Texture Creation: Functions like CreateWICTextureFromFile place data into an intermediate upload heap and record the necessary copy commands.
    3. End(commandQueue): Submits the recorded copy commands to the specified GPU command queue and returns a std::future<void>.
    4. Synchronization: Calling .wait() on the returned future blocks the CPU until the GPU has finished the upload, ensuring the resource is ready for rendering.
  6. How to use the GraphicsMemory class

    main

    In DirectX 12, the application is responsible for managing the lifetime of video memory resources. The DirectX::GraphicsMemory class is a helper for managing dynamic allocations (such as constant buffers, dynamic vertex/index buffers, and upload heaps) using an 'upload heap' pattern.

    Lifecycle Management

    1. Initialization: Create the instance once using the device.
    2. Per-Frame Update: Call Commit once per frame after presenting to ensure proper tracking and cleanup of resources via fences.
    3. Cleanup: Reset the pointer during device loss events.

    Implementation Pattern

    In your header (e.g., Game.h):

    std::unique_ptr<DirectX::GraphicsMemory> m_graphicsMemory;

    In your device creation (e.g., Game.cpp):

    m_graphicsMemory = std::make_unique<GraphicsMemory>(device);

    In your render loop (e.g., Game.cpp):

    // Show the new frame.
    m_deviceResources->Present();
    m_graphicsMemory->Commit(m_deviceResources->GetCommandQueue());

    In your device loss handler (e.g., Game.cpp):

    m_graphicsMemory.reset();
  7. Understand the DeviceResources abstraction

    main

    In the DirectXTK12 tutorial architecture, the DeviceResources class acts as a wrapper for Direct3D 12 device and swap chain management. It encapsulates the following core components:

    • IDXGIFactory6: Used for adapter enumeration, swap chain creation, and checking feature support (HDR, tearing, etc.).
    • ID3D12Device: The factory for all GPU resources.
    • ID3D12CommandQueue: The queue used to submit command lists to the GPU.
    • ID3D12CommandAllocator: Backing memory for command list recording (one per frame in flight).
    • ID3D12GraphicsCommandList: Used to record rendering commands (draw calls, resource barriers).
    • IDXGISwapChain3: Manages presentation using DXGI_SWAP_EFFECT_FLIP_DISCARD.
    • ID3D12Resource: Includes the swap chain back buffers (render targets) and the depth stencil resource.
    • ID3D12DescriptorHeap: Manals the RTV (Render Target View) and DSV (Depth Stencil View) descriptors.
    • ID3D12Fence: Provides CPU/GPU synchronization to ensure resources are not overwritten while in use.
    • Debug layer: Configures ID3D12InfoQueue in debug builds to catch errors.
  8. Render 3D shapes with GeometricPrimitive

    main

    The GeometricPrimitive class provides procedurally generated 3D meshes (spheres, cubes, teapots, etc.). Unlike PrimitiveBatch, which uses dynamic buffers, GeometricPrimitive uses indexed triangle meshes and can be optimized by uploading its vertex and index buffers to the default heap (GPU-only memory) for better performance.

    Key Differences from PrimitiveBatch:

    • Buffer type: GeometricPrimitive uses static buffers (can be in the default heap), whereas PrimitiveBatch uses dynamic buffers (upload heap).
    • Index buffer: GeometricPrimitive always uses an index buffer.
    • Shader setup: In DirectX 12, you must manually create and apply an effect (e.g., BasicEffect) to render GeometricPrimitive objects. The DX11 version handled this automatically, but DX12 requires explicit management.
  9. Coordinate Systems in BasicEffect

    main

    By default, BasicEffect uses Normalized Device Coordinates (NDC), where the visible range is -1 to +1 on both the X and Y axes, and the origin (0,0) is at the center of the screen.

    To use Pixel Coordinates (where the origin is at the top-left and the Y-axis points down, matching SpriteBatch), you must apply an orthographic projection matrix using m_effect->SetProjection():

    Matrix proj = Matrix::CreateOrthographicOffCenter(
        0.f, 
        float(screenSize.right), 
        float(screenSize.bottom), 
        0.f, 
        0.f, 
        1.f
    );
    m_effect->SetProjection(proj);
  10. How PrimitiveBatch works as an immediate-mode renderer

    main

    PrimitiveBatch<T> is a lightweight, immediate-mode geometry renderer designed for dynamic geometry.

    Key Characteristics:

    • Dynamic Buffers: It manages its own vertex and index buffers using GraphicsMemory, allocating space from the upload heap every frame. You do not need to manage persistent GPU buffers for your vertices.
    • No Shader Management: Unlike SpriteBatch, PrimitiveBatch does not manage the pipeline state. You are responsible for calling IEffect::Apply(commandList) before calling m_batch->Begin().
    • Topology: The primitive topology is determined by the specific draw method used (e.g., DrawTriangle uses D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, while DrawLine uses LINELIST).

    Lifecycle:

    1. m_batch->Begin(commandList): Prepares the batch for new geometry.
    2. m_batch->Draw...(...): Copies vertex data into the dynamic buffer and records the draw command.
    3. m_batch->End(): Flushes the remaining batched geometry to the command list.
  11. Configure Pipeline State for Effects

    main

    To create Pipeline State Objects (PSOs) for effects, DirectXTK12 uses lightweight description structures:

    • RenderTargetState (Inc/RenderTargetState.h): Communicates render target format and sample count information.
    • EffectPipelineStateDescription (Inc/EffectPipelineStateDescription.h): A bundle containing input layout, blend state, depth-stencil state, rasterizer state, and RenderTargetState. This description is passed to effect constructors.
  12. Manage relative mouse movement with Mouse class

    main

    The Mouse class implementation of relative mouse movement accumulates changes between calls to GetState.

    • Default behavior: Each call to GetState resets the deltas. This is ideal if you call GetState once per frame.
    • Manual reset: If you call GetState multiple times per frame and want to control when deltas are reset, call EndOfInputFrame explicitly.