148 lines
9.9 KiB
Markdown
148 lines
9.9 KiB
Markdown
# 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.
|
|
|
|
```cpp
|
|
// ... 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:
|
|
1. Rendering commands (`Draw`, etc.) typically operate on `RENDER_TARGET` or `COPY_SRC/DST` states depending on context, but transitioning immediately allows for faster access without additional fences if the renderer supports it.
|
|
2. It prepares the texture to be read by CPU (if using staging buffers) and written to again in subsequent frame passes via copy commands (`ResourceBarrier` with `D3D12_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.
|
|
|
|
```cpp
|
|
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.
|
|
|
|
```cpp
|
|
// 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 `0` for 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 table `SwapchainCompositionToColorSpace`. It dynamically sets color space and format (e.g., RGB10A2 for HDR).
|
|
* **Tearing Support:** Checks `driver-?IsTearingSupported` to set the `DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING` flag if present, preventing frame skips when V-Sync is off.
|
|
|
|
**2. Factory Interactions ? WMA (Windows Meta Association):**
|
|
```cpp
|
|
// 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 `Resize` functions 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:
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:** `CreateSwapChainForHwnd` creates an interface and returns it. The caller is expected to release *one* copy of that interface eventually. By calling `QueryInterface`, 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 (like `DestroySwapChain`).
|
|
|
|
#### 🚨 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:
|
|
```cpp
|
|
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
|
|
|
|
1. **Validate WMA Logic:** Ensure `DXGI_MWA_NO_WINDOW_CHANGES` doesn'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.
|
|
2. **Interface Management:** The immediate release of the original Swap Chain pointer after acquiring `IDXGISwapChain3` looks 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.
|
|
3. **Error Handling:** The `CreateSwapChainForHwnd` call checks for failure, but if it fails due to invalid window handle (`IsWindow` check 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)`
|
|
|