using Ghost.Graphics.Data;
using Ghost.Graphics.RenderGraphModule;
using System.Numerics;
using Win32.Graphics.Direct3D12;
using Win32.Graphics.Dxgi.Common;
namespace Ghost.Graphics.Examples;
///
/// Example demonstrating render graph usage with history buffers and multiple cameras
///
public static class RenderGraphExample
{
// Example pass data structures
public struct DepthPrePassData
{
public RGTextureHandle DepthBuffer;
// Add other render targets, constants, etc.
}
public struct GBufferPassData
{
public RGTextureHandle DepthBuffer;
public RGTextureHandle AlbedoBuffer;
public RGTextureHandle NormalBuffer;
public RGTextureHandle MaterialBuffer;
}
public struct LightingPassData
{
public RGTextureHandle DepthBuffer;
public RGTextureHandle AlbedoBuffer;
public RGTextureHandle NormalBuffer;
public RGTextureHandle MaterialBuffer;
public RGTextureHandle SceneColor;
public RGTextureHandle PreviousFrameColor; // History buffer
}
public struct PostProcessPassData
{
public RGTextureHandle SceneColor;
public RGTextureHandle FinalColor;
}
///
/// Example camera/view state class to store history buffers (like Unreal's FViewState)
///
public class CameraViewState
{
public TextureHandle? PreviousFrameColorBuffer;
public TextureHandle? PreviousFrameDepthBuffer;
public TextureHandle? VelocityBuffer;
public Matrix4x4 PreviousViewProjectionMatrix;
}
public static void ExampleRenderGraph(CameraViewState viewState, TextureHandle backBuffer)
{
// Create render graph with optional descriptor
var renderGraphDesc = new RenderGraphDesc(initialResourceCapacity: 512, initialPassCapacity: 128);
using var renderGraph = new RenderGraph("MainRenderGraph", renderGraphDesc);
// Begin recording passes
renderGraph.BeginRecord();
// Import external resources (backbuffer, history buffers)
var backBufferHandle = renderGraph.ImportTexture(
"BackBuffer",
backBuffer,
new TextureDescription(1920, 1080, 1, Format.R8G8B8A8Unorm, ResourceFlags.AllowRenderTarget));
// Import history buffer if available (for temporal effects like TAA)
RGTextureHandle? previousFrameColorHandle = null;
if (viewState.PreviousFrameColorBuffer.HasValue)
{
previousFrameColorHandle = renderGraph.ImportTexture(
"PreviousFrameColor",
viewState.PreviousFrameColorBuffer.Value,
new TextureDescription(1920, 1080, 1, Format.R8G8B8A8Unorm));
}
// Depth Pre-pass
renderGraph.CreatePass("DepthPrePass")
.Setup((ref DepthPrePassData data, RenderPassBuilder builder) =>
{
// Create depth buffer as transient resource
data.DepthBuffer = builder.CreateTexture("DepthBuffer",
new TextureDescription(1920, 1080, 1, Format.D32Float, ResourceFlags.AllowDepthStencil));
// Declare write access to depth buffer
data.DepthBuffer = builder.WriteTexture(data.DepthBuffer);
})
.SetRenderFunc((ref DepthPrePassData data, RenderPassContext ctx) =>
{
// Render depth only
// Use data.DepthBuffer for rendering
})
.Compile();
// G-Buffer Pass
var gBufferOutputs = renderGraph.CreatePass("GBufferPass")
.Setup((ref GBufferPassData data, RenderPassBuilder builder) =>
{
// Read depth buffer from previous pass
data.DepthBuffer = builder.ReadTexture(data.DepthBuffer); // This will be resolved during compilation
// Create G-Buffer targets
data.AlbedoBuffer = builder.CreateTexture("AlbedoBuffer",
new TextureDescription(1920, 1080, 1, Format.R8G8B8A8Unorm, ResourceFlags.AllowRenderTarget));
data.NormalBuffer = builder.CreateTexture("NormalBuffer",
new TextureDescription(1920, 1080, 1, Format.R8G8B8A8Snorm, ResourceFlags.AllowRenderTarget));
data.MaterialBuffer = builder.CreateTexture("MaterialBuffer",
new TextureDescription(1920, 1080, 1, Format.R8G8B8A8Unorm, ResourceFlags.AllowRenderTarget));
// Declare write access
data.AlbedoBuffer = builder.WriteTexture(data.AlbedoBuffer);
data.NormalBuffer = builder.WriteTexture(data.NormalBuffer);
data.MaterialBuffer = builder.WriteTexture(data.MaterialBuffer);
})
.SetRenderFunc((ref GBufferPassData data, RenderPassContext ctx) =>
{
// Render geometry to G-Buffer
// Use data.DepthBuffer, data.AlbedoBuffer, etc.
});
gBufferOutputs.Compile();
// Lighting Pass
var lightingOutput = renderGraph.CreatePass("LightingPass")
.Setup((ref LightingPassData data, RenderPassBuilder builder) =>
{
// Read G-Buffer outputs
data.DepthBuffer = builder.ReadTexture(data.DepthBuffer);
data.AlbedoBuffer = builder.ReadTexture(data.AlbedoBuffer);
data.NormalBuffer = builder.ReadTexture(data.NormalBuffer);
data.MaterialBuffer = builder.ReadTexture(data.MaterialBuffer);
// Create scene color output
data.SceneColor = builder.CreateTexture("SceneColor",
new TextureDescription(1920, 1080, 1, Format.R16G16B16A16Float, ResourceFlags.AllowRenderTarget));
data.SceneColor = builder.WriteTexture(data.SceneColor);
// Use previous frame color if available (for temporal effects)
if (previousFrameColorHandle.HasValue)
{
data.PreviousFrameColor = builder.ReadTexture(previousFrameColorHandle.Value);
}
})
.SetRenderFunc((ref LightingPassData data, RenderPassContext ctx) =>
{
// Perform deferred lighting
// Can access previous frame color for temporal anti-aliasing, etc.
});
lightingOutput.Compile();
// Post-Processing Pass
renderGraph.CreatePass("PostProcessPass")
.Setup((ref PostProcessPassData data, RenderPassBuilder builder) =>
{
// Read scene color from lighting pass
data.SceneColor = builder.ReadTexture(data.SceneColor);
// Write to backbuffer
data.FinalColor = builder.WriteTexture(backBufferHandle);
})
.SetRenderFunc((ref PostProcessPassData data, RenderPassContext ctx) =>
{
// Apply tone mapping, gamma correction, etc.
// Copy to backbuffer
});
// End recording
renderGraph.EndRecord();
// Compile the render graph (dependency analysis, topological sort, resource lifetime analysis)
renderGraph.Compile();
// Export resources for next frame (history buffers)
if (lightingOutput != null) // In real implementation, you'd track the scene color handle
{
// viewState.PreviousFrameColorBuffer = renderGraph.ExportTexture(sceneColorHandle);
}
// Execute the render graph
renderGraph.Execute();
}
///
/// Example with multiple cameras rendering to different targets
///
public static void MultiCameraExample(List cameras, List renderTargets)
{
using var renderGraph = new RenderGraph("MultiCameraRenderGraph");
renderGraph.BeginRecord();
for (var i = 0; i < cameras.Count; i++)
{
var camera = cameras[i];
var renderTarget = renderTargets[i];
var cameraName = $"Camera{i}";
// Import render target
var renderTargetHandle = renderGraph.ImportTexture(
$"{cameraName}_RenderTarget",
renderTarget,
new TextureDescription(1920, 1080, 1, Format.R8G8B8A8Unorm, ResourceFlags.AllowRenderTarget));
// Camera-specific rendering passes
renderGraph.CreatePass($"{cameraName}_DepthPrePass")
.Setup((ref DepthPrePassData data, RenderPassBuilder builder) =>
{
data.DepthBuffer = builder.CreateTexture($"{cameraName}_DepthBuffer",
new TextureDescription(1920, 1080, 1, Format.D32Float, ResourceFlags.AllowDepthStencil));
data.DepthBuffer = builder.WriteTexture(data.DepthBuffer);
})
.SetRenderFunc((ref DepthPrePassData data, RenderPassContext ctx) =>
{
// Render with camera[i] view/projection matrices
})
.Compile();
// Additional passes for this camera...
}
renderGraph.EndRecord();
renderGraph.Compile();
renderGraph.Execute();
}
}