forked from Misaki/GhostEngine
Update rendering architecture and resource management
Added a new `Ref<T>` struct for reference semantics. Added the `RenderGraph` system for managing rendering passes. Added the `RenderTexture` class for encapsulating GPU resources. Added `GraphicsBuffer` class for effective GPU resource management. Changed `CommandList` methods from public to internal for visibility control. Changed `IRenderPass` interface from internal to public for accessibility. Changed `GetData<T>()` in `ComponentObject.cs` to return `CompRef<T>`. Changed `GetComponent<T>()` in `EntityManager.cs` to return `CompRef<T>`. Changed `GetSingleton<T>()` in `World.cs` to use `CompRef<T>`. Changed `IQueryTypeParameter` to use `CompRef<T>` for consistency. Changed `QueryItem<T0>` and related structs to use `CompRef<T>`. Changed `Material` class to support bindless textures. Changed `Shader` class to support bindless rendering. Changed `Mesh` class to support bindless vertex and index buffer access. Updated documentation to reflect the new bindless rendering architecture.
This commit is contained in:
@@ -1,178 +0,0 @@
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.Examples;
|
||||
|
||||
/// <summary>
|
||||
/// Example demonstrating how to use the IDescriptorAllocator interface.
|
||||
/// </summary>
|
||||
public static class DescriptorAllocatorExample
|
||||
{
|
||||
/// <summary>
|
||||
/// Example showing basic descriptor allocation and release.
|
||||
/// </summary>
|
||||
public static void BasicDescriptorUsage()
|
||||
{
|
||||
// Note: In a real application, you would get the descriptor allocator from the graphics device
|
||||
// var device = GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>();
|
||||
// var descriptorAllocator = device.DescriptorAllocator;
|
||||
|
||||
// For demonstration purposes, we'll show the interface usage
|
||||
DescriptorAllocator descriptorAllocator = null!; // Would be obtained from graphics device
|
||||
|
||||
// Allocate a single RTV descriptor
|
||||
var rtvDescriptor = descriptorAllocator.AllocateRTV();
|
||||
|
||||
// Allocate multiple SRV descriptors
|
||||
var srvDescriptors = descriptorAllocator.AllocateSRVs(4);
|
||||
|
||||
// Allocate a DSV descriptor
|
||||
var dsvDescriptor = descriptorAllocator.AllocateDSV();
|
||||
|
||||
// Allocate sampler descriptors
|
||||
var samplerDescriptors = descriptorAllocator.AllocateSamplers(2);
|
||||
|
||||
// Use the descriptors...
|
||||
ProcessDescriptors(rtvDescriptor, srvDescriptors, dsvDescriptor, samplerDescriptors);
|
||||
|
||||
// Release descriptors when done
|
||||
descriptorAllocator.ReleaseRTV(rtvDescriptor);
|
||||
descriptorAllocator.ReleaseSRVs(srvDescriptors);
|
||||
descriptorAllocator.ReleaseDSV(dsvDescriptor);
|
||||
descriptorAllocator.ReleaseSamplers(samplerDescriptors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example showing how to work with D3D12-specific descriptor handles.
|
||||
/// </summary>
|
||||
public static void D3D12SpecificUsage()
|
||||
{
|
||||
// In D3D12-specific code, you can cast to get the underlying handles
|
||||
DescriptorAllocator descriptorAllocator = null!; // Would be obtained from graphics device
|
||||
|
||||
var srvDescriptor = descriptorAllocator.AllocateSRV();
|
||||
|
||||
// For D3D12, you can cast to get the specific handles
|
||||
if (srvDescriptor is ShaderResourceDescriptor d3d12Srv)
|
||||
{
|
||||
var cpuHandle = d3d12Srv.CpuHandle;
|
||||
var gpuHandle = d3d12Srv.GpuHandle;
|
||||
|
||||
// Use the handles for D3D12 API calls
|
||||
// device.CreateShaderResourceView(resource, &srvDesc, cpuHandle);
|
||||
// commandList.SetGraphicsRootDescriptorTable(0, gpuHandle);
|
||||
}
|
||||
|
||||
descriptorAllocator.ReleaseSRV(srvDescriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example showing descriptor management in a texture class.
|
||||
/// </summary>
|
||||
public static void TextureDescriptorUsage()
|
||||
{
|
||||
// This shows how you might integrate descriptor allocation in a texture class
|
||||
DescriptorAllocator descriptorAllocator = null!; // Would be obtained from graphics device
|
||||
|
||||
// Create a texture with SRV
|
||||
var texture = new ExampleTexture2D(512, 512, Format.R8G8B8A8Unorm, descriptorAllocator);
|
||||
|
||||
// The texture automatically manages its SRV descriptor
|
||||
var srvDescriptor = texture.GetShaderResourceView();
|
||||
|
||||
// Use the texture...
|
||||
|
||||
// Dispose will automatically release the descriptor
|
||||
texture.Dispose();
|
||||
}
|
||||
|
||||
private static void ProcessDescriptors(
|
||||
RenderTargetDescriptor rtv,
|
||||
ShaderResourceDescriptor[] srvs,
|
||||
DepthStencilDescriptor dsv,
|
||||
SamplerDescriptor[] samplers)
|
||||
{
|
||||
// Process the descriptors - check validity, shader visibility, etc.
|
||||
if (rtv.IsValid)
|
||||
{
|
||||
// Use RTV descriptor
|
||||
}
|
||||
|
||||
foreach (var srv in srvs)
|
||||
{
|
||||
if (srv.IsValid && srv.IsShaderVisible)
|
||||
{
|
||||
// Use SRV descriptor in shaders
|
||||
}
|
||||
}
|
||||
|
||||
if (dsv.IsValid)
|
||||
{
|
||||
// Use DSV descriptor
|
||||
}
|
||||
|
||||
foreach (var sampler in samplers)
|
||||
{
|
||||
if (sampler.IsValid && sampler.IsShaderVisible)
|
||||
{
|
||||
// Use sampler descriptor in shaders
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example texture class showing descriptor integration.
|
||||
/// </summary>
|
||||
internal class ExampleTexture2D : IDisposable
|
||||
{
|
||||
private readonly DescriptorAllocator _descriptorAllocator;
|
||||
private readonly ShaderResourceDescriptor _srvDescriptor;
|
||||
private readonly uint _width;
|
||||
private readonly uint _height;
|
||||
private readonly Format _format;
|
||||
private bool _disposed;
|
||||
|
||||
public ExampleTexture2D(uint width, uint height, Format format, DescriptorAllocator descriptorAllocator)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_format = format;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
|
||||
// Allocate SRV descriptor for this texture
|
||||
_srvDescriptor = _descriptorAllocator.AllocateSRV();
|
||||
|
||||
// Create the actual texture resource and SRV...
|
||||
CreateTextureResource();
|
||||
}
|
||||
|
||||
public ShaderResourceDescriptor GetShaderResourceView() => _srvDescriptor;
|
||||
|
||||
private void CreateTextureResource()
|
||||
{
|
||||
// Create D3D12 texture resource
|
||||
// Create SRV using the allocated descriptor
|
||||
|
||||
// For D3D12:
|
||||
if (_srvDescriptor is ShaderResourceDescriptor d3d12Srv)
|
||||
{
|
||||
var cpuHandle = d3d12Srv.CpuHandle;
|
||||
// device.CreateShaderResourceView(textureResource, &srvDesc, cpuHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
// Release the descriptor
|
||||
_descriptorAllocator.ReleaseSRV(_srvDescriptor);
|
||||
|
||||
// Dispose texture resource...
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
230
Ghost.Graphics/Examples/RenderGraphExample.cs
Normal file
230
Ghost.Graphics/Examples/RenderGraphExample.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using Ghost.Graphics.Data;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using System.Numerics;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.Examples;
|
||||
|
||||
/// <summary>
|
||||
/// Example demonstrating render graph usage with history buffers and multiple cameras
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example camera/view state class to store history buffers (like Unreal's FViewState)
|
||||
/// </summary>
|
||||
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<DepthPrePassData>("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<GBufferPassData>("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<LightingPassData>("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<PostProcessPassData>("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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Example with multiple cameras rendering to different targets
|
||||
/// </summary>
|
||||
public static void MultiCameraExample(List<CameraViewState> cameras, List<TextureHandle> 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<DepthPrePassData>($"{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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user