Refactor and enhance graphics and audio systems
Updated target frameworks to .NET 10.0 across multiple projects for compatibility with the latest features. Refactored namespaces and introduced new classes for shader descriptors, FMOD integration, and DirectX 12 utilities using TerraFX. Replaced `Win32` bindings with TerraFX equivalents for DirectX 12. Added a C# wrapper for FMOD Studio API, including DSP and error handling. Enhanced entity queries, component storage, and query filters for better performance and type safety. Introduced new test projects and updated the solution structure. Added `meshoptimizer` bindings and integrated `meshoptimizer_native.dll`. Improved code readability, maintainability, and performance.
This commit is contained in:
@@ -1,418 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Descriptor for render graph configuration
|
||||
/// </summary>
|
||||
public readonly struct RenderGraphDesc
|
||||
{
|
||||
public readonly int InitialResourceCapacity;
|
||||
public readonly int InitialPassCapacity;
|
||||
|
||||
public RenderGraphDesc(int initialResourceCapacity = 256, int initialPassCapacity = 64)
|
||||
{
|
||||
InitialResourceCapacity = initialResourceCapacity;
|
||||
InitialPassCapacity = initialPassCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Main render graph class for managing transient resources and render passes
|
||||
/// </summary>
|
||||
public sealed class RenderGraph : IDisposable
|
||||
{
|
||||
private readonly string _name;
|
||||
private readonly List<RenderGraphResource> _resources;
|
||||
private readonly List<RenderPass> _passes;
|
||||
private readonly List<int> _compiledPassOrder;
|
||||
private readonly List<RenderGraphBarrier> _barriers;
|
||||
|
||||
private bool _isRecording;
|
||||
private bool _isCompiled;
|
||||
private bool _isExecuted;
|
||||
private bool _disposed;
|
||||
|
||||
public RenderGraph(string name, RenderGraphDesc desc = default)
|
||||
{
|
||||
_name = name;
|
||||
_resources = new(desc.InitialResourceCapacity > 0 ? desc.InitialResourceCapacity : 256);
|
||||
_passes = new(desc.InitialPassCapacity > 0 ? desc.InitialPassCapacity : 64);
|
||||
_compiledPassOrder = new();
|
||||
_barriers = new();
|
||||
}
|
||||
|
||||
public string Name => _name;
|
||||
public bool IsRecording => _isRecording;
|
||||
public bool IsCompiled => _isCompiled;
|
||||
|
||||
/// <summary>
|
||||
/// Begin recording render passes
|
||||
/// </summary>
|
||||
public void BeginRecord()
|
||||
{
|
||||
if (_isRecording)
|
||||
throw new InvalidOperationException("Render graph is already recording");
|
||||
if (_isCompiled)
|
||||
throw new InvalidOperationException("Cannot record on a compiled render graph");
|
||||
|
||||
_isRecording = true;
|
||||
_isExecuted = false;
|
||||
|
||||
// Clear previous frame data
|
||||
foreach (var resource in _resources)
|
||||
{
|
||||
if (resource.Lifetime == ResourceLifetime.Transient)
|
||||
{
|
||||
resource.ReleaseResource();
|
||||
}
|
||||
}
|
||||
|
||||
_resources.RemoveAll(r => r.Lifetime == ResourceLifetime.Transient);
|
||||
_passes.Clear();
|
||||
_compiledPassOrder.Clear();
|
||||
_barriers.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End recording render passes
|
||||
/// </summary>
|
||||
public void EndRecord()
|
||||
{
|
||||
if (!_isRecording)
|
||||
throw new InvalidOperationException("Render graph is not recording");
|
||||
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new render pass
|
||||
/// </summary>
|
||||
public RenderPassCreator<TPassData> CreatePass<TPassData>(string passName)
|
||||
where TPassData : struct
|
||||
{
|
||||
if (!_isRecording)
|
||||
throw new InvalidOperationException("Cannot create pass when not recording");
|
||||
|
||||
return new RenderPassCreator<TPassData>(this, passName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a transient texture resource
|
||||
/// </summary>
|
||||
public RGTextureHandle CreateTexture(string name, ResourceLifetime lifetime, TextureDescription description)
|
||||
{
|
||||
var texture = new RenderGraphTexture(_resources.Count, name, lifetime, description);
|
||||
|
||||
_resources.Add(texture);
|
||||
return new RGTextureHandle(texture.Id, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a transient buffer resource
|
||||
/// </summary>
|
||||
public RGBufferHandle CreateBuffer(string name, ResourceLifetime lifetime, BufferDescription description)
|
||||
{
|
||||
var buffer = new RenderGraphBuffer(_resources.Count, name, lifetime, description);
|
||||
|
||||
_resources.Add(buffer);
|
||||
return new RGBufferHandle(buffer.Id, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import an external texture (e.g., from previous frame, swap chain)
|
||||
/// </summary>
|
||||
public RGTextureHandle ImportTexture(string name, Handle<Texture> externalHandle, TextureDescription description)
|
||||
{
|
||||
var texture = new RenderGraphTexture(_resources.Count, name, externalHandle, description);
|
||||
|
||||
_resources.Add(texture);
|
||||
return new RGTextureHandle(texture.Id, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import an external buffer (e.g., from previous frame)
|
||||
/// </summary>
|
||||
public RGBufferHandle ImportBuffer(string name, Handle<GraphicsBuffer> externalHandle, BufferDescription description)
|
||||
{
|
||||
var buffer = new RenderGraphBuffer(_resources.Count, name, externalHandle, description);
|
||||
|
||||
_resources.Add(buffer);
|
||||
return new RGBufferHandle(buffer.Id, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export a resource for use in the next frame (for history buffers)
|
||||
/// </summary>
|
||||
public Handle<Texture> ExportTexture(RGTextureHandle handle)
|
||||
{
|
||||
if (!handle.IsValid || handle._resourceId >= _resources.Count)
|
||||
throw new ArgumentException("Invalid texture handle", nameof(handle));
|
||||
|
||||
var texture = (RenderGraphTexture)_resources[handle._resourceId];
|
||||
texture.Lifetime = ResourceLifetime.Persistent;
|
||||
return texture.Handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export a buffer for use in the next frame
|
||||
/// </summary>
|
||||
public Handle<GraphicsBuffer> ExportBuffer(RGBufferHandle handle)
|
||||
{
|
||||
if (!handle.IsValid || handle._resourceId >= _resources.Count)
|
||||
throw new ArgumentException("Invalid buffer handle", nameof(handle));
|
||||
|
||||
var buffer = (RenderGraphBuffer)_resources[handle._resourceId];
|
||||
buffer.Lifetime = ResourceLifetime.Persistent;
|
||||
return buffer.Handle;
|
||||
}
|
||||
|
||||
public RenderGraphTexture GetTextureResource(RGTextureHandle handle)
|
||||
{
|
||||
if (!handle.IsValid || handle._resourceId >= _resources.Count)
|
||||
throw new ArgumentException("Invalid texture handle", nameof(handle));
|
||||
return (RenderGraphTexture)_resources[handle._resourceId];
|
||||
}
|
||||
|
||||
public RenderGraphBuffer GetBufferResource(RGBufferHandle handle)
|
||||
{
|
||||
if (!handle.IsValid || handle._resourceId >= _resources.Count)
|
||||
throw new ArgumentException("Invalid buffer handle", nameof(handle));
|
||||
return (RenderGraphBuffer)_resources[handle._resourceId];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to add a pass to the render graph
|
||||
/// </summary>
|
||||
internal void AddPass(RenderPass pass)
|
||||
{
|
||||
pass.Index = _passes.Count;
|
||||
_passes.Add(pass);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to update resource lifetime during pass setup
|
||||
/// </summary>
|
||||
internal void UpdateResourceLifetime(int resourceId, int passIndex)
|
||||
{
|
||||
if (resourceId >= _resources.Count)
|
||||
return;
|
||||
|
||||
var resource = _resources[resourceId];
|
||||
if (resource.FirstPassIndex == -1)
|
||||
resource.FirstPassIndex = passIndex;
|
||||
|
||||
resource.LastPassIndex = Math.Max(resource.LastPassIndex, passIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile the render graph - performs dependency analysis, topological sort, and resource lifetime analysis
|
||||
/// </summary>
|
||||
public void Compile()
|
||||
{
|
||||
if (_isRecording)
|
||||
throw new InvalidOperationException("Cannot compile while recording");
|
||||
if (_isCompiled)
|
||||
throw new InvalidOperationException("Render graph is already compiled");
|
||||
|
||||
// Setup all passes to gather resource dependencies
|
||||
SetupPasses();
|
||||
|
||||
// Build dependency graph and perform topological sort
|
||||
BuildDependencyGraph();
|
||||
TopologicalSort();
|
||||
|
||||
// Analyze resource lifetimes and generate barriers
|
||||
AnalyzeResourceLifetimes();
|
||||
GenerateBarriers();
|
||||
|
||||
_isCompiled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute the compiled render graph
|
||||
/// </summary>
|
||||
public void Execute()
|
||||
{
|
||||
if (!_isCompiled)
|
||||
throw new InvalidOperationException("Render graph must be compiled before execution");
|
||||
if (_isExecuted)
|
||||
throw new InvalidOperationException("Render graph has already been executed");
|
||||
|
||||
// Execute passes in topological order
|
||||
foreach (var passIndex in _compiledPassOrder)
|
||||
{
|
||||
var pass = _passes[passIndex];
|
||||
var context = new RenderPassContext(passIndex);
|
||||
|
||||
CreateTransientResources(passIndex);
|
||||
ApplyBarriersForPass(passIndex);
|
||||
|
||||
// Execute the pass
|
||||
pass.Execute(context);
|
||||
}
|
||||
|
||||
_isExecuted = true;
|
||||
}
|
||||
|
||||
private void SetupPasses()
|
||||
{
|
||||
for (var i = 0; i < _passes.Count; i++)
|
||||
{
|
||||
var pass = _passes[i];
|
||||
var builder = new RenderPassBuilder(this, i);
|
||||
pass.Setup(builder);
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildDependencyGraph()
|
||||
{
|
||||
// Build dependencies based on resource usage
|
||||
foreach (var pass in _passes)
|
||||
{
|
||||
var writeResources = new HashSet<int>();
|
||||
var readResources = new HashSet<int>();
|
||||
|
||||
// Categorize resource accesses
|
||||
foreach (var access in pass.ResourceAccesses)
|
||||
{
|
||||
switch (access.accessType)
|
||||
{
|
||||
case ResourceAccessType.Write:
|
||||
writeResources.Add(access.resourceId);
|
||||
break;
|
||||
case ResourceAccessType.Read:
|
||||
readResources.Add(access.resourceId);
|
||||
break;
|
||||
case ResourceAccessType.ReadWrite:
|
||||
writeResources.Add(access.resourceId);
|
||||
readResources.Add(access.resourceId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add dependencies based on Write-After-Read, Read-After-Write, Write-After-Write
|
||||
for (var otherPassIndex = 0; otherPassIndex < pass.Index; otherPassIndex++)
|
||||
{
|
||||
var otherPass = _passes[otherPassIndex];
|
||||
var hasDependency = false;
|
||||
|
||||
foreach (var otherAccess in otherPass.ResourceAccesses)
|
||||
{
|
||||
// WAR, RAW, WAW dependencies
|
||||
if ((writeResources.Contains(otherAccess.resourceId) && otherAccess.accessType == ResourceAccessType.Read) ||
|
||||
(readResources.Contains(otherAccess.resourceId) && otherAccess.accessType != ResourceAccessType.Read) ||
|
||||
(writeResources.Contains(otherAccess.resourceId) && otherAccess.accessType != ResourceAccessType.Read))
|
||||
{
|
||||
hasDependency = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDependency && !pass.Dependencies.Contains(otherPassIndex))
|
||||
{
|
||||
pass.Dependencies.Add(otherPassIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TopologicalSort()
|
||||
{
|
||||
_compiledPassOrder.Clear();
|
||||
var visited = new bool[_passes.Count];
|
||||
var inDegree = new int[_passes.Count];
|
||||
|
||||
// Calculate in-degrees
|
||||
foreach (var pass in _passes)
|
||||
{
|
||||
foreach (var dependency in pass.Dependencies)
|
||||
{
|
||||
inDegree[pass.Index]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Kahn's algorithm for topological sorting
|
||||
var queue = new Queue<int>();
|
||||
for (var i = 0; i < _passes.Count; i++)
|
||||
{
|
||||
if (inDegree[i] == 0)
|
||||
{
|
||||
queue.Enqueue(i);
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var passIndex = queue.Dequeue();
|
||||
_compiledPassOrder.Add(passIndex);
|
||||
|
||||
var currentPass = _passes[passIndex];
|
||||
foreach (var dependentPassIndex in _passes.Where(p => p.Dependencies.Contains(passIndex)).Select(p => p.Index))
|
||||
{
|
||||
inDegree[dependentPassIndex]--;
|
||||
if (inDegree[dependentPassIndex] == 0)
|
||||
{
|
||||
queue.Enqueue(dependentPassIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_compiledPassOrder.Count != _passes.Count)
|
||||
{
|
||||
throw new InvalidOperationException("Circular dependency detected in render graph");
|
||||
}
|
||||
}
|
||||
|
||||
private void AnalyzeResourceLifetimes()
|
||||
{
|
||||
// Resource lifetimes are already tracked during pass setup
|
||||
// Additional analysis can be added here if needed
|
||||
}
|
||||
|
||||
private void GenerateBarriers()
|
||||
{
|
||||
_barriers.Clear();
|
||||
|
||||
// TODO: Implement barrier generation based on resource state transitions
|
||||
// This would analyze the resource usage patterns and generate appropriate D3D12 barriers
|
||||
}
|
||||
|
||||
private void CreateTransientResources(int passIndex)
|
||||
{
|
||||
var pass = _passes[passIndex];
|
||||
foreach (var access in pass.ResourceAccesses)
|
||||
{
|
||||
var resource = _resources[access.resourceId];
|
||||
if (!resource.IsCreated)
|
||||
{
|
||||
resource.CreateResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyBarriersForPass(int passIndex)
|
||||
{
|
||||
// TODO: Apply the generated barriers for the given pass
|
||||
// This would involve creating D3D12 resource barriers and executing them on the command list
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
foreach (var resource in _resources)
|
||||
{
|
||||
resource.ReleaseResource();
|
||||
}
|
||||
|
||||
_resources.Clear();
|
||||
_passes.Clear();
|
||||
_compiledPassOrder.Clear();
|
||||
_barriers.Clear();
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Handle for render graph texture resource
|
||||
/// </summary>
|
||||
public readonly struct RGTextureHandle : IEquatable<RGTextureHandle>
|
||||
{
|
||||
internal readonly RenderGraph? _renderGraph;
|
||||
internal readonly int _resourceId;
|
||||
|
||||
internal RGTextureHandle(int resourceId, RenderGraph renderGraph)
|
||||
{
|
||||
_resourceId = resourceId;
|
||||
_renderGraph = renderGraph;
|
||||
}
|
||||
|
||||
public bool IsValid => _resourceId >= 0 && _renderGraph != null;
|
||||
|
||||
public bool Equals(RGTextureHandle other)
|
||||
{
|
||||
return _resourceId == other._resourceId && ReferenceEquals(_renderGraph, other._renderGraph);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is RGTextureHandle other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_resourceId, _renderGraph);
|
||||
}
|
||||
|
||||
public static bool operator ==(RGTextureHandle left, RGTextureHandle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(RGTextureHandle left, RGTextureHandle right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle for render graph buffer resource
|
||||
/// </summary>
|
||||
public readonly struct RGBufferHandle : IEquatable<RGBufferHandle>
|
||||
{
|
||||
internal readonly RenderGraph? _renderGraph;
|
||||
internal readonly int _resourceId;
|
||||
|
||||
internal RGBufferHandle(int resourceId, RenderGraph renderGraph)
|
||||
{
|
||||
_resourceId = resourceId;
|
||||
_renderGraph = renderGraph;
|
||||
}
|
||||
|
||||
public bool IsValid => _resourceId >= 0 && _renderGraph != null;
|
||||
|
||||
public bool Equals(RGBufferHandle other)
|
||||
{
|
||||
return _resourceId == other._resourceId && ReferenceEquals(_renderGraph, other._renderGraph);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is RGBufferHandle other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_resourceId, _renderGraph);
|
||||
}
|
||||
|
||||
public static bool operator ==(RGBufferHandle left, RGBufferHandle right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(RGBufferHandle left, RGBufferHandle right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource access information for dependency tracking
|
||||
/// </summary>
|
||||
internal readonly struct ResourceAccess
|
||||
{
|
||||
public readonly int resourceId;
|
||||
public readonly int passIndex;
|
||||
public readonly ResourceAccessType accessType;
|
||||
|
||||
public ResourceAccess(int resourceId, ResourceAccessType accessType, int passIndex)
|
||||
{
|
||||
this.resourceId = resourceId;
|
||||
this.accessType = accessType;
|
||||
this.passIndex = passIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a barrier to be executed for resource state transitions
|
||||
/// </summary>
|
||||
internal readonly struct RenderGraphBarrier
|
||||
{
|
||||
public readonly int resourceId;
|
||||
public readonly ResourceStates stateBefore;
|
||||
public readonly ResourceStates stateAfter;
|
||||
|
||||
public RenderGraphBarrier(int resourceId, ResourceStates stateBefore, ResourceStates stateAfter)
|
||||
{
|
||||
this.resourceId = resourceId;
|
||||
this.stateBefore = stateBefore;
|
||||
this.stateAfter = stateAfter;
|
||||
}
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Data;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Represents different resource access types in the render graph
|
||||
/// </summary>
|
||||
public enum ResourceAccessType
|
||||
{
|
||||
Read,
|
||||
Write,
|
||||
ReadWrite
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource lifetime within the render graph
|
||||
/// </summary>
|
||||
public enum ResourceLifetime
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource is created and destroyed within a single frame
|
||||
/// </summary>
|
||||
Transient,
|
||||
/// <summary>
|
||||
/// Resource is imported from external source (e.g., history buffers, backbuffer)
|
||||
/// </summary>
|
||||
External,
|
||||
/// <summary>
|
||||
/// Resource persists across multiple frames (exported for next frame)
|
||||
/// </summary>
|
||||
Persistent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for render graph resources
|
||||
/// </summary>
|
||||
public abstract class RenderGraphResource
|
||||
{
|
||||
public int FirstPassIndex
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal int LastPassIndex
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public int Id
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ResourceLifetime Lifetime
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
|
||||
public bool IsImported
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public bool IsCreated
|
||||
{
|
||||
get; protected set;
|
||||
}
|
||||
|
||||
protected RenderGraphResource(int id, string name, ResourceLifetime lifetime)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Lifetime = lifetime;
|
||||
FirstPassIndex = -1;
|
||||
LastPassIndex = -1;
|
||||
}
|
||||
|
||||
internal abstract void CreateResource();
|
||||
internal abstract void ReleaseResource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a texture resource in the render graph
|
||||
/// </summary>
|
||||
public sealed class RenderGraphTexture : RenderGraphResource
|
||||
{
|
||||
internal Handle<Texture> Handle
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal TextureDescription Description
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public RenderGraphTexture(int id, string name, ResourceLifetime lifetime, TextureDescription description)
|
||||
: base(id, name, lifetime)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public RenderGraphTexture(int id, string name, Handle<Texture> handle, TextureDescription description)
|
||||
: base(id, name, ResourceLifetime.External)
|
||||
{
|
||||
Handle = handle;
|
||||
Description = description;
|
||||
IsImported = true;
|
||||
IsCreated = true;
|
||||
}
|
||||
|
||||
internal override void CreateResource()
|
||||
{
|
||||
if (IsCreated || IsImported)
|
||||
return;
|
||||
|
||||
var allocFlags = Lifetime == ResourceLifetime.Transient
|
||||
? Win32.Graphics.D3D12MemoryAllocator.AllocationFlags.CanAlias
|
||||
: Win32.Graphics.D3D12MemoryAllocator.AllocationFlags.None;
|
||||
|
||||
Handle = GraphicsPipeline.ResourceAllocator.CreateTexture2D(
|
||||
Description.Width,
|
||||
Description.Height,
|
||||
Description.MipLevels,
|
||||
Description.Format,
|
||||
Description.Flags,
|
||||
allocFlags,
|
||||
Description.InitialState);
|
||||
|
||||
IsCreated = true;
|
||||
}
|
||||
|
||||
internal override void ReleaseResource()
|
||||
{
|
||||
if (!IsCreated || IsImported)
|
||||
return;
|
||||
|
||||
Handle.Dispose();
|
||||
IsCreated = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a buffer resource in the render graph
|
||||
/// </summary>
|
||||
public sealed class RenderGraphBuffer : RenderGraphResource
|
||||
{
|
||||
internal Handle<GraphicsBuffer> Handle
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
internal BufferDescription Description
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public RenderGraphBuffer(int id, string name, ResourceLifetime lifetime, BufferDescription description)
|
||||
: base(id, name, lifetime)
|
||||
{
|
||||
Description = description;
|
||||
}
|
||||
public RenderGraphBuffer(int id, string name, Handle<GraphicsBuffer> handle, BufferDescription description)
|
||||
: base(id, name, ResourceLifetime.External)
|
||||
{
|
||||
Handle = handle;
|
||||
Description = description;
|
||||
IsImported = true;
|
||||
IsCreated = true;
|
||||
}
|
||||
|
||||
internal override void CreateResource()
|
||||
{
|
||||
if (IsCreated || IsImported)
|
||||
return;
|
||||
|
||||
var allocFlags = Lifetime == ResourceLifetime.Transient
|
||||
? Win32.Graphics.D3D12MemoryAllocator.AllocationFlags.CanAlias
|
||||
: Win32.Graphics.D3D12MemoryAllocator.AllocationFlags.None;
|
||||
|
||||
Handle = GraphicsPipeline.ResourceAllocator.CreateBuffer(
|
||||
Description.SizeInBytes,
|
||||
Description.HeapType,
|
||||
Description.Flags,
|
||||
allocFlags,
|
||||
Description.InitialState);
|
||||
|
||||
IsCreated = true;
|
||||
}
|
||||
|
||||
internal override void ReleaseResource()
|
||||
{
|
||||
if (!IsCreated || IsImported)
|
||||
return;
|
||||
|
||||
Handle.Dispose();
|
||||
IsCreated = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture description for render graph texture creation
|
||||
/// </summary>
|
||||
public readonly struct TextureDescription
|
||||
{
|
||||
public readonly uint Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly uint Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ushort MipLevels
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly Format Format
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ResourceFlags Flags
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ResourceStates InitialState
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public TextureDescription(uint width, uint height, ushort mipLevels = 1,
|
||||
Format format = Format.R8G8B8A8Unorm, ResourceFlags flags = ResourceFlags.None,
|
||||
ResourceStates initialState = ResourceStates.Common)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
MipLevels = mipLevels;
|
||||
Format = format;
|
||||
Flags = flags;
|
||||
InitialState = initialState;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Buffer description for render graph buffer creation
|
||||
/// </summary>
|
||||
public readonly struct BufferDescription
|
||||
{
|
||||
public readonly uint SizeInBytes
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly HeapType HeapType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ResourceFlags Flags
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly ResourceStates InitialState
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public BufferDescription(uint sizeInBytes, HeapType heapType = HeapType.Default,
|
||||
ResourceFlags flags = ResourceFlags.None, ResourceStates initialState = ResourceStates.Common)
|
||||
{
|
||||
SizeInBytes = sizeInBytes;
|
||||
HeapType = heapType;
|
||||
Flags = flags;
|
||||
InitialState = initialState;
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for pass setup function
|
||||
/// </summary>
|
||||
public delegate void PassSetupFunction<TPassData>(ref TPassData data, RenderPassBuilder builder) where TPassData : struct;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for pass execution function
|
||||
/// </summary>
|
||||
public delegate void PassExecuteFunction<TPassData>(ref TPassData data, RenderPassContext context) where TPassData : struct;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for render passes in the render graph
|
||||
/// </summary>
|
||||
internal abstract class RenderPass
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
public int Index
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
public List<ResourceAccess> ResourceAccesses
|
||||
{
|
||||
get;
|
||||
}
|
||||
public List<int> Dependencies
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
protected RenderPass(string name)
|
||||
{
|
||||
Name = name;
|
||||
ResourceAccesses = new List<ResourceAccess>();
|
||||
Dependencies = new List<int>();
|
||||
}
|
||||
|
||||
public abstract void Setup(RenderPassBuilder builder);
|
||||
public abstract void Execute(RenderPassContext context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Typed render pass implementation
|
||||
/// </summary>
|
||||
internal sealed class RenderPass<TPassData> : RenderPass
|
||||
where TPassData : struct
|
||||
{
|
||||
private readonly PassSetupFunction<TPassData> _setupFunction;
|
||||
private readonly PassExecuteFunction<TPassData> _executeFunction;
|
||||
private TPassData _passData;
|
||||
|
||||
public RenderPass(string name, PassSetupFunction<TPassData> setupFunction, PassExecuteFunction<TPassData> executeFunction)
|
||||
: base(name)
|
||||
{
|
||||
_setupFunction = setupFunction;
|
||||
_executeFunction = executeFunction;
|
||||
}
|
||||
|
||||
public override void Setup(RenderPassBuilder builder)
|
||||
{
|
||||
_setupFunction(ref _passData, builder);
|
||||
ResourceAccesses.AddRange(builder.ResourceAccesses);
|
||||
}
|
||||
|
||||
public override void Execute(RenderPassContext context)
|
||||
{
|
||||
_executeFunction(ref _passData, context);
|
||||
}
|
||||
|
||||
public void SetPassData(TPassData passData)
|
||||
{
|
||||
_passData = passData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builder for creating render passes
|
||||
/// </summary>
|
||||
public sealed class RenderPassCreator<TPassData>
|
||||
where TPassData : struct
|
||||
{
|
||||
private readonly RenderGraph _renderGraph;
|
||||
private readonly string _passName;
|
||||
private PassSetupFunction<TPassData>? _setupFunction;
|
||||
private PassExecuteFunction<TPassData>? _executeFunction;
|
||||
|
||||
internal RenderPassCreator(RenderGraph renderGraph, string passName)
|
||||
{
|
||||
_renderGraph = renderGraph;
|
||||
_passName = passName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the setup function for the render pass
|
||||
/// </summary>
|
||||
public RenderPassCreator<TPassData> Setup(PassSetupFunction<TPassData> setupFunction)
|
||||
{
|
||||
_setupFunction = setupFunction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the render function for the render pass
|
||||
/// </summary>
|
||||
public RenderPassCreator<TPassData> SetRenderFunc(PassExecuteFunction<TPassData> executeFunction)
|
||||
{
|
||||
_executeFunction = executeFunction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Compile()
|
||||
{
|
||||
if (_setupFunction == null)
|
||||
throw new InvalidOperationException($"Setup function not set for pass '{_passName}'");
|
||||
if (_executeFunction == null)
|
||||
throw new InvalidOperationException($"Execute function not set for pass '{_passName}'");
|
||||
|
||||
var pass = new RenderPass<TPassData>(_passName, _setupFunction, _executeFunction);
|
||||
_renderGraph.AddPass(pass);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Context passed to render pass execution functions
|
||||
/// </summary>
|
||||
public readonly struct RenderPassContext
|
||||
{
|
||||
internal readonly int PassIndex;
|
||||
// TODO: Add command list and other rendering context when available
|
||||
|
||||
internal RenderPassContext(int passIndex)
|
||||
{
|
||||
PassIndex = passIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builder for configuring render pass resource dependencies
|
||||
/// </summary>
|
||||
public sealed class RenderPassBuilder
|
||||
{
|
||||
private readonly RenderGraph _renderGraph;
|
||||
private readonly int _passIndex;
|
||||
private readonly List<ResourceAccess> _resourceAccesses;
|
||||
|
||||
internal RenderPassBuilder(RenderGraph renderGraph, int passIndex)
|
||||
{
|
||||
_renderGraph = renderGraph;
|
||||
_passIndex = passIndex;
|
||||
_resourceAccesses = new List<ResourceAccess>();
|
||||
}
|
||||
|
||||
internal IReadOnlyList<ResourceAccess> ResourceAccesses => _resourceAccesses;
|
||||
|
||||
/// <summary>
|
||||
/// Declare a texture read dependency
|
||||
/// </summary>
|
||||
public RGTextureHandle ReadTexture(RGTextureHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
throw new ArgumentException("Invalid texture handle", nameof(handle));
|
||||
|
||||
_resourceAccesses.Add(new ResourceAccess(handle._resourceId, ResourceAccessType.Read, _passIndex));
|
||||
_renderGraph.UpdateResourceLifetime(handle._resourceId, _passIndex);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare a texture write dependency
|
||||
/// </summary>
|
||||
public RGTextureHandle WriteTexture(RGTextureHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
throw new ArgumentException("Invalid texture handle", nameof(handle));
|
||||
|
||||
_resourceAccesses.Add(new ResourceAccess(handle._resourceId, ResourceAccessType.Write, _passIndex));
|
||||
_renderGraph.UpdateResourceLifetime(handle._resourceId, _passIndex);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare a texture read-write dependency
|
||||
/// </summary>
|
||||
public RGTextureHandle ReadWriteTexture(RGTextureHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
throw new ArgumentException("Invalid texture handle", nameof(handle));
|
||||
|
||||
_resourceAccesses.Add(new ResourceAccess(handle._resourceId, ResourceAccessType.ReadWrite, _passIndex));
|
||||
_renderGraph.UpdateResourceLifetime(handle._resourceId, _passIndex);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare a buffer read dependency
|
||||
/// </summary>
|
||||
public RGBufferHandle ReadBuffer(RGBufferHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
throw new ArgumentException("Invalid buffer handle", nameof(handle));
|
||||
|
||||
_resourceAccesses.Add(new ResourceAccess(handle._resourceId, ResourceAccessType.Read, _passIndex));
|
||||
_renderGraph.UpdateResourceLifetime(handle._resourceId, _passIndex);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare a buffer write dependency
|
||||
/// </summary>
|
||||
public RGBufferHandle WriteBuffer(RGBufferHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
throw new ArgumentException("Invalid buffer handle", nameof(handle));
|
||||
|
||||
_resourceAccesses.Add(new ResourceAccess(handle._resourceId, ResourceAccessType.Write, _passIndex));
|
||||
_renderGraph.UpdateResourceLifetime(handle._resourceId, _passIndex);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare a buffer read-write dependency
|
||||
/// </summary>
|
||||
public RGBufferHandle ReadWriteBuffer(RGBufferHandle handle)
|
||||
{
|
||||
if (!handle.IsValid)
|
||||
throw new ArgumentException("Invalid buffer handle", nameof(handle));
|
||||
|
||||
_resourceAccesses.Add(new ResourceAccess(handle._resourceId, ResourceAccessType.ReadWrite, _passIndex));
|
||||
_renderGraph.UpdateResourceLifetime(handle._resourceId, _passIndex);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new transient texture within this pass
|
||||
/// </summary>
|
||||
public RGTextureHandle CreateTexture(string name, TextureDescription description)
|
||||
{
|
||||
return _renderGraph.CreateTexture(name, ResourceLifetime.Transient, description);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new transient buffer within this pass
|
||||
/// </summary>
|
||||
public RGBufferHandle CreateBuffer(string name, BufferDescription description)
|
||||
{
|
||||
return _renderGraph.CreateBuffer(name, ResourceLifetime.Transient, description);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user