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:
2025-08-01 21:34:48 +09:00
parent 1284bb17de
commit eafbfb2fa1
43 changed files with 3845 additions and 2183 deletions

View File

@@ -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);
}
}

View 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();
}
}