9.9 KiB
Juliet\src\Graphics\D3D12\D3D12SwapChain
Source Files
- Header:
Juliet\src\Graphics\D3D12\D3D12SwapChain.h - Source:
Juliet\src\Graphics\D3D12\D3D12SwapChain.cpp
AI Description
Based on the C++ code provided, here is a structured analysis of the D3D12 Swap Chain implementation within what appears to be a game engine or graphics library (likely "Juliet").
1. Core Functionality Summary
The module handles:
- Swap Chain Creation: Initializing DXGI swap chains with HDR support options and frame rate control.
- Texture Management: Managing the backing D3D12 textures for each back buffer, transitioning states between Presentation (
PRESENT) and Rendering (RENDER_TARGET). - Fence Synchronization: Ensuring previous frames are finished before starting new ones to prevent tearing or accessing dirty buffers during rendering.
2. Detailed Code Analysis by Function
A. State Transition ? Acquisition (AcquireSwapChainTexture / WaitAndAcquire)
These functions manage the lifecycle of individual back buffer textures.
// ... inside AcquireSwapChainTexture logic
barrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrierDesc.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; // Source state (from GPU)
barrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; // Destination state (for CPU/GPU read/write)
// The transition barrier is issued on the command list.
d3d12CommandList-?GraphicsCommandList.CommandList-?ResourceBarrier(1, ?barrierDesc);
*swapchainTexture = reinterpret_cast?Texture*?(?windowData-?SwapChainTextureContainers[...].ActiveTexture); // Returns a pointer wrapper to access TextureDetails struct directly.
Key Insight: The code explicitly transitions the resource from PRESENT (owned by DXGI) back to RENDER_TARGET. This is standard practice because:
- Rendering commands (
Draw, etc.) typically operate onRENDER_TARGETorCOPY_SRC/DSTstates depending on context, but transitioning immediately allows for faster access without additional fences if the renderer supports it. - It prepares the texture to be read by CPU (if using staging buffers) and written to again in subsequent frame passes via copy commands (
ResourceBarrierwithD3D12_RESOURCE_BARRIER_FLAG_ALLOW_VISUAL_LOSS_DISCARD).
B. Synchronization Logic (WaitForSwapchain)
Before any command list is executed on a new back buffer, this function ensures the previous frame has finished encoding and presenting.
if (windowData-?InFlightFences[windowData-?WindowFrameCounter] != nullptr) {
if (!Wait(d3d12Driver, true, ?windowData-?InFlightFences[...], 1 JULIET_DEBUG_PARAM(...))) return false;
}
return true;
Key Insight: This checks the fence associated with the specific frame being processed. If a fence exists (meaning previous work is pending), it waits for that fence to signal completion before proceeding.
C. Format Resolution ? Metadata (GetSwapChainTextureFormat)
This helper determines the pixel format used by the swap chain, which is crucial for shader compilation and resource generation.
// Returns TextureFormat enum derived from DXGI_FORMAT of active buffer
auto* d3d12Driver = static_cast?D3D12Driver*?(driver.Get());
return windowData-?SwapChainTextureContainers[windowData-?WindowFrameCounter].Header.CreateInfo.Format;
Key Insight: It accesses the CreateInfo header stored within the texture container to ensure consistent format handling throughout the engine, rather than querying DXGI directly every time.
D. Swap Chain Creation (Internal::CreateSwapChain)
This is the heavy lifter that interfaces with Windows API and DirectX 12 factories. Notable features:
1. Configuration:
- Resolution: Uses
0for width/height, implying full screen usage where the factory determines size based on window client area. - Present Mode ? Composition: Supports SDR (
BG8) or HDR via a mapping tableSwapchainCompositionToColorSpace. It dynamically sets color space and format (e.g., RGB10A2 for HDR). - Tearing Support: Checks
driver-?IsTearingSupportedto set theDXGI_SWAP_CHAIN_FLAG_ALLOW_TEARINGflag if present, preventing frame skips when V-Sync is off.
2. Factory Interactions ? WMA (Windows Meta Association):
// Queries for IDXGISwapChain3 interface needed for HDR settings and specific behaviors.
IDXGISwapChain3* swapChain3 = nullptr;
...
swapChain-?QueryInterface(IID_IDXGISwapChain3, ...); // Note: The original CreateSwapChainForHwnd returns a 1 pointer usually, but here it queries then releases the old one?
// Correction in logic analysis: The code does 'query', assigns to swapChain3 variable, and calls Release on swapChain immediately. This implies swapChain returned by QueryInterface was temporary or they are managing two pointers (original + queried) briefly before swapping roles.
if (!parentFactory-?MakeWindowAssociation(windowHandle, DXGI_MWA_NO_WINDOW_CHANGES))
Log(LogLevel::Warning, ... "Cannot handle window resizing"); // IMPORTANT: Disables automatic resize messages to D3D12 logic?
Key Insight on WMA: The code calls DXGI_MWA_NO_WINDOW_CHANGES. This tells the system not to send a WM_SIZE message when the user resizes or moves the window.
- Why? If the app handles resizing by simply calling
Resizefunctions internally without checking Win32 events, this is an optimization (avoiding extra logic). However, if dynamic re-resizing is needed externally, this needs to be reconsidered based on how else size changes are handled in your engine.
3. HDR Setup:
It specifically sets the color space only for SwapChainComposition::HDR or other non-SDR types:
if (composition != SwapChainComposition::SDR) {
swapChain3-?SetColorSpace1(SwapchainCompositionToColorSpace[...]);
}
3. Critical Logic Check ? Observations
🚨 Potential Issue in CreateSwapChain Interface Handling
There is a slight logical flow quirk regarding the Swap Chain pointer assignment:
IDXGISwapChain1* swapChain = nullptr;
// Creates chain, returns as IDXGISwapChain1 (or higher if capabilities met)
HRESULT result = driver-?DXGIFactory-?CreateSwapChainForHwnd(..., ?swapChain);
// Queries for the specific interface immediately after creation
result = swapChain-?QueryInterface(IID_IDXGISwapChain3, reinterpret_cast?void**?(?swapChain3));
swapChain-?Release(); // ?--- Releases the original pointer passed by reference?
if (FAILED(result)) { ... }
- Behavior:
CreateSwapChainForHwndcreates an interface and returns it. The caller is expected to release one copy of that interface eventually. By callingQueryInterface, you create a new handle (swapChain3). If the original return value from Create doesn't support QueryInterface directly (unlikely) or if memory management isn't strict, releasing via Release() on the temporary variable might be premature depending on D3D12 ownership rules here. - Standard Pattern: Usually:
Create...(?SwapChain), then use it until done with all interfaces required for HDR/Feature queries before doing cleanup logic in other parts of your engine's destroy sequence (likeDestroySwapChain).
🚨 Memory Management ? Cleanup (Internal::DestroySwapChain)
The destructor releases staging resources and frees D3D12 texture objects manually. This indicates a custom resource pool system rather than just relying on DXGI to clean up everything at shutdown, which is good for performance profiling but requires careful Free implementations (likely global Free lists common in Rust/Juliet ecosystems).
📝 Resource Barrier Nuance
In the transition block:
barrierDesc.Transition.pResource = ...; // Pointer math looks correct based on struct layout.
barrierDesc.Transition.Subresource = 0;
// Note: Since this is not a multi-sub-resource texture (like video textures), Subresource=0 covers everything.
4. Recommendations / Improvements
- Validate WMA Logic: Ensure
DXGI_MWA_NO_WINDOW_CHANGESdoesn't break dynamic resolution support in your game engine. If the window moves/resizes but you don't update texture dimensions, it could cause black screens or crashes upon resize. - Interface Management: The immediate release of the original Swap Chain pointer after acquiring
IDXGISwapChain3looks unusual if the engine expects to keep using generic features later without re-acquisition every time (e.g., in a destructor). Consider storing the generic interface and only converting up/down when HDR is specifically accessed or during teardown. - Error Handling: The
CreateSwapChainForHwndcall checks for failure, but if it fails due to invalid window handle (IsWindowcheck passed earlier), ensure robust logging regarding why (e.g., focus lost, incompatible OS version).
Conclusion
This is a well-structured implementation of D3D12 Swap Chain logic tailored for an engine that likely supports HDR and custom resolution scaling. The explicit fence waiting and manual state transitions indicate high control over the rendering pipeline to ensure frame timing consistency and prevent visual
Symbols
Namespace Juliet::D3D12
Functions & Methods
extern bool AcquireSwapChainTexture(NonNullPtr<CommandList> commandList, NonNullPtr<Window> window, Texture** swapChainTexture)extern bool WaitAndAcquireSwapChainTexture(NonNullPtr<CommandList> commandList, NonNullPtr<Window> window, Texture** swapChainTexture)extern bool WaitForSwapchain(NonNullPtr<GPUDriver> driver, NonNullPtr<Window> window)extern TextureFormat GetSwapChainTextureFormat(NonNullPtr<GPUDriver> driver, NonNullPtr<Window> window)
Namespace Juliet::D3D12::Internal
Functions & Methods
extern bool CreateSwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData, SwapChainComposition composition, PresentMode presentMode)extern void DestroySwapChain(NonNullPtr<D3D12Driver> driver, NonNullPtr<D3D12WindowData> windowData)