Refactor render graph error handling and resource APIs
- RenderGraph.Compile/Execute now return Error for better failure detection; error handling is propagated throughout compiler and executor. - Renamed ScheduleReleaseResource to ReleaseResource for clarity; updated all usages. - ResourceManager now calls ReleaseResource directly on Mesh, Material, and Shader types. - Camera exposes Actual/Virtual size properties and Render returns Error. - RenderingContext now uses IResourceManager for mesh/resource ops. - Replaced custom BinaryWriter with BufferWriter in RenderGraphHasher. - Improved variable naming, interface signatures, and code formatting. - Added Error extension for IsSuccess/IsFailure. - Minor FMOD/native interop and test code cleanups. - No breaking API changes except for new Error return values on some methods.
This commit is contained in:
@@ -8,6 +8,7 @@ public class Camera
|
||||
{
|
||||
private readonly IRenderer _renderer;
|
||||
|
||||
// History buffers.
|
||||
private Handle<Texture> _colorTexture;
|
||||
private Handle<Texture> _depthTexture;
|
||||
|
||||
@@ -23,14 +24,17 @@ public class Camera
|
||||
/// Gets the actual width of the camera's render target in pixels. If upscaler is used, this is the width before upscaling.
|
||||
/// </summary>
|
||||
public uint ActualWidth => _actualWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual height of the camera's render target in pixels. If upscaler is used, this is the height before upscaling.
|
||||
/// </summary>
|
||||
public uint ActualHeight => _actualHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the virtual width of the camera's render target in pixels. If upscaler is used, this is the width after upscaling.
|
||||
/// </summary>
|
||||
public uint VirtualWidth => _virtualWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the virtual height of the camera's render target in pixels. If upscaler is used, this is the height after upscaling.
|
||||
/// </summary>
|
||||
@@ -64,8 +68,17 @@ public class Camera
|
||||
RenderGraph.Reset();
|
||||
|
||||
var view = new ViewState(_virtualWidth, _virtualHeight, _actualWidth, _actualHeight);
|
||||
RenderGraph.Compile(in view);
|
||||
RenderGraph.Execute(context.CommandBuffer);
|
||||
var e = RenderGraph.Compile(in view);
|
||||
if (e != Error.None)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
e = RenderGraph.Execute(context.CommandBuffer);
|
||||
if (e != Error.None)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ internal struct CBufferCache : IResourceReleasable
|
||||
}
|
||||
|
||||
_cpuData.Dispose();
|
||||
database.ScheduleReleaseResource(_gpuResource.AsResource());
|
||||
database.ReleaseResource(_gpuResource.AsResource());
|
||||
|
||||
_gpuResource = Handle<GraphicsBuffer>.Invalid;
|
||||
_size = 0;
|
||||
@@ -144,7 +144,6 @@ public struct Material : IResourceReleasable
|
||||
return _cBufferCache.CpuData.AsSpan(0, (int)_cBufferCache.Size);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public unsafe Error SetPropertyCache<T>(scoped ref readonly T data)
|
||||
where T : unmanaged
|
||||
{
|
||||
@@ -166,7 +165,6 @@ public struct Material : IResourceReleasable
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Error SetRawPropertyCache(ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (data.Length != _cBufferCache.Size)
|
||||
@@ -200,7 +198,6 @@ public struct Material : IResourceReleasable
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Error SetKeyword(IResourceManager manager, int keywordId, bool enabled)
|
||||
{
|
||||
var r = manager.GetShaderReference(_shader);
|
||||
@@ -222,7 +219,6 @@ public struct Material : IResourceReleasable
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool IsKeywordEnabled(IResourceManager manager, int keywordId)
|
||||
{
|
||||
var r = manager.GetShaderReference(_shader);
|
||||
@@ -276,8 +272,7 @@ public struct Material : IResourceReleasable
|
||||
cmd.ResourceBarrier(desc);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||
public void ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
_cBufferCache.ReleaseResource(database);
|
||||
_passPipelineOverride.Dispose();
|
||||
|
||||
@@ -113,13 +113,13 @@ public struct Mesh : IResourceReleasable
|
||||
_indices.Dispose();
|
||||
}
|
||||
|
||||
readonly void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||
public readonly void ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
ReleaseCpuResources();
|
||||
|
||||
database.ScheduleReleaseResource(VertexBuffer.AsResource());
|
||||
database.ScheduleReleaseResource(IndexBuffer.AsResource());
|
||||
database.ScheduleReleaseResource(ObjectDataBuffer.AsResource());
|
||||
database.ReleaseResource(VertexBuffer.AsResource());
|
||||
database.ReleaseResource(IndexBuffer.AsResource());
|
||||
database.ReleaseResource(ObjectDataBuffer.AsResource());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,21 @@ namespace Ghost.Graphics.Core;
|
||||
public readonly unsafe ref struct RenderingContext
|
||||
{
|
||||
private readonly IGraphicsEngine _engine;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly ICommandBuffer _directCmd;
|
||||
|
||||
public ICommandBuffer DirectCommandBuffer => _directCmd;
|
||||
|
||||
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
|
||||
public IResourceManager ResourceManager => _resourceManager;
|
||||
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
||||
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
||||
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
||||
|
||||
internal RenderingContext(IGraphicsEngine engine, ICommandBuffer directCmd)
|
||||
internal RenderingContext(IGraphicsEngine engine, IResourceManager resourceManager, ICommandBuffer directCmd)
|
||||
{
|
||||
_engine = engine;
|
||||
_resourceManager = resourceManager;
|
||||
_directCmd = directCmd;
|
||||
}
|
||||
|
||||
@@ -82,8 +85,8 @@ public readonly unsafe ref struct RenderingContext
|
||||
|
||||
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices, bool staticMesh)
|
||||
{
|
||||
var mesh = ResourceAllocator.CreateMesh(vertices, indices);
|
||||
var r = ResourceDatabase.GetMeshReference(mesh);
|
||||
var mesh = _resourceManager.CreateMesh(vertices, indices);
|
||||
var r = _resourceManager.GetMeshReference(mesh);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return mesh;
|
||||
@@ -129,7 +132,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
/// <param name="markMeshStatic">Whether to mark the mesh as static. If it's true, the cpu buffer of the mesh will not be avaliable any more</param>
|
||||
public void UploadMesh(Handle<Mesh> mesh, bool markMeshStatic)
|
||||
{
|
||||
var r = ResourceDatabase.GetMeshReference(mesh);
|
||||
var r = _resourceManager.GetMeshReference(mesh);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
@@ -156,7 +159,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
|
||||
public void UpdateObjectData(Handle<Mesh> mesh, float4x4 localToWorld)
|
||||
{
|
||||
var r = ResourceDatabase.GetMeshReference(mesh);
|
||||
var r = _resourceManager.GetMeshReference(mesh);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
@@ -192,7 +195,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
where T : unmanaged
|
||||
{
|
||||
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()).GetValueOrThrow();
|
||||
|
||||
|
||||
//var size = ResourceAllocator.GetSizeInfo(desc).Size;
|
||||
//if ((ulong)(data.Length * sizeof(T)) != ResourceAllocator.GetSizeInfo(desc).Size)
|
||||
//{
|
||||
|
||||
@@ -3,6 +3,7 @@ using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
@@ -136,6 +137,7 @@ public partial struct Shader : IResourceReleasable
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal readonly int GetLocalKeywordIndex(int globalKeywordID)
|
||||
{
|
||||
if (_keywordIDToLocal.TryGetValue(globalKeywordID, out var localIndex))
|
||||
@@ -146,6 +148,7 @@ public partial struct Shader : IResourceReleasable
|
||||
return -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly int GetPassIndex(Identifier<ShaderPass> passID)
|
||||
{
|
||||
if (_passIDToLocal.TryGetValue(passID.Value, out var index))
|
||||
@@ -156,6 +159,7 @@ public partial struct Shader : IResourceReleasable
|
||||
return -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly int GetPassIndex(string passName)
|
||||
{
|
||||
if (_passIDToLocal.TryGetValue(GetPassID(passName), out var index))
|
||||
@@ -166,11 +170,13 @@ public partial struct Shader : IResourceReleasable
|
||||
return -1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly ref ShaderPass GetPassReference(int index)
|
||||
{
|
||||
return ref _shaderPasses[index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Result<ShaderPass, Error> TryGetPass(Identifier<ShaderPass> passID, out int passIndex)
|
||||
{
|
||||
if (_passIDToLocal.TryGetValue(passID.Value, out var index))
|
||||
@@ -183,7 +189,7 @@ public partial struct Shader : IResourceReleasable
|
||||
return _shaderPasses[index];
|
||||
}
|
||||
|
||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||
public void ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
_keywordIDToLocal.Dispose();
|
||||
_shaderPasses.Dispose();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
internal unsafe class GPUResourceLeakException : Exception
|
||||
|
||||
@@ -5,7 +5,6 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Main render graph class that manages resource allocation and pass execution.
|
||||
/// Delegates complex operations to specialized components for better organization.
|
||||
/// </summary>
|
||||
public sealed class RenderGraph : IDisposable
|
||||
{
|
||||
@@ -172,38 +171,38 @@ public sealed class RenderGraph : IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Compiles the render graph by culling unused passes and determining resource lifetimes.
|
||||
/// Delegates to RenderGraphCompiler for the actual compilation work.
|
||||
/// </summary>
|
||||
public void Compile(in ViewState viewState)
|
||||
public Error Compile(in ViewState viewState)
|
||||
{
|
||||
if (_compiled)
|
||||
{
|
||||
return;
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
// Resolve texture sizes before computing hash
|
||||
_resources.ResolveTextureSizes(in viewState);
|
||||
|
||||
// Compute structural hash for caching
|
||||
var graphHash = RenderGraphHasher.ComputeGraphHash(_passes, _resources);
|
||||
var error = _compiler.Compile(in viewState, graphHash, _passes, _compiledPasses, _nativePasses, _compiledBarriers);
|
||||
if (error != Error.None)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// Delegate to compiler
|
||||
_compiler.Compile(in viewState, graphHash, _passes, _compiledPasses, _nativePasses, _compiledBarriers);
|
||||
_compiled = true;
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all compiled passes using native render passes where possible.
|
||||
/// Delegates to RenderGraphExecutor for the actual execution work.
|
||||
/// </summary>
|
||||
public void Execute(ICommandBuffer cmd)
|
||||
public Error Execute(ICommandBuffer cmd)
|
||||
{
|
||||
if (!_compiled)
|
||||
{
|
||||
throw new InvalidOperationException("Render graph must be compiled before execution. Call Compile(viewState) first.");
|
||||
return Error.InvalidState;
|
||||
}
|
||||
|
||||
_executor.Execute(cmd, _compiledPasses, _nativePasses, _compiledBarriers);
|
||||
return _executor.Execute(cmd, _compiledPasses, _nativePasses, _compiledBarriers);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -94,7 +94,7 @@ internal sealed class ResourceHeap
|
||||
{
|
||||
// Check if this offset range conflicts with ANY existing allocations
|
||||
var canUseOffset = CanPlaceAtOffset(alignedOffset, alignedSize, firstUsePass, lastUsePass);
|
||||
|
||||
|
||||
if (canUseOffset)
|
||||
{
|
||||
var waste = block.size - alignedSize;
|
||||
@@ -173,7 +173,7 @@ internal sealed class ResourceHeap
|
||||
private bool CanPlaceAtOffset(ulong offset, ulong size, int firstUsePass, int lastUsePass)
|
||||
{
|
||||
var endOffset = offset + size;
|
||||
|
||||
|
||||
foreach (var block in _blocks)
|
||||
{
|
||||
// Skip free blocks - they don't have lifetime constraints
|
||||
@@ -185,12 +185,12 @@ internal sealed class ResourceHeap
|
||||
// Check if this block's memory range overlaps with our target range
|
||||
var blockEnd = block.offset + block.size;
|
||||
var memoryOverlap = !(offset >= blockEnd || endOffset <= block.offset);
|
||||
|
||||
|
||||
if (memoryOverlap)
|
||||
{
|
||||
// Memory ranges overlap, check if lifetimes also overlap
|
||||
var lifetimeOverlap = !(firstUsePass > block.lastUsePass || lastUsePass < block.firstUsePass);
|
||||
|
||||
|
||||
if (lifetimeOverlap)
|
||||
{
|
||||
// Both memory AND lifetime overlap - cannot place here!
|
||||
@@ -198,7 +198,7 @@ internal sealed class ResourceHeap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ internal sealed class ResourceAliasingManager
|
||||
private const ulong _DEFAULT_BUFFER_ALIGNMENT = 65536; // 64KB for D3D12
|
||||
|
||||
public ResourceHeap Heap => _heap;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to get the size of a resource
|
||||
/// </summary>
|
||||
@@ -374,8 +374,8 @@ internal sealed class ResourceAliasingManager
|
||||
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||
{
|
||||
var size = GetResourceSize(logicalResource);
|
||||
var alignment = logicalResource.type == RenderGraphResourceType.Texture
|
||||
? _DEFAULT_TEXTURE_ALIGNMENT
|
||||
var alignment = logicalResource.type == RenderGraphResourceType.Texture
|
||||
? _DEFAULT_TEXTURE_ALIGNMENT
|
||||
: _DEFAULT_BUFFER_ALIGNMENT;
|
||||
|
||||
var (success, offset, block) = simulationHeap.TryAllocate(
|
||||
@@ -393,7 +393,7 @@ internal sealed class ResourceAliasingManager
|
||||
|
||||
// Get peak usage from simulation
|
||||
var peakMemoryUsage = simulationHeap.GetPeakUsage();
|
||||
|
||||
|
||||
// Align peak usage to 64KB (D3D12 requirement)
|
||||
peakMemoryUsage = AlignUp(peakMemoryUsage, _DEFAULT_TEXTURE_ALIGNMENT);
|
||||
|
||||
@@ -405,8 +405,8 @@ internal sealed class ResourceAliasingManager
|
||||
foreach (var (logicalIndex, logicalResource) in logicalResources)
|
||||
{
|
||||
var size = GetResourceSize(logicalResource);
|
||||
var alignment = logicalResource.type == RenderGraphResourceType.Texture
|
||||
? _DEFAULT_TEXTURE_ALIGNMENT
|
||||
var alignment = logicalResource.type == RenderGraphResourceType.Texture
|
||||
? _DEFAULT_TEXTURE_ALIGNMENT
|
||||
: _DEFAULT_BUFFER_ALIGNMENT;
|
||||
|
||||
var (success, offset, block) = _heap.TryAllocate(
|
||||
@@ -444,7 +444,7 @@ internal sealed class ResourceAliasingManager
|
||||
for (var i = 0; i < _placedResources.Count; i++)
|
||||
{
|
||||
var placed = _placedResources[i];
|
||||
|
||||
|
||||
// Find all logical resources that share the same heap location
|
||||
for (var j = 0; j < _placedResources.Count; j++)
|
||||
{
|
||||
@@ -454,7 +454,7 @@ internal sealed class ResourceAliasingManager
|
||||
}
|
||||
|
||||
var other = _placedResources[j];
|
||||
|
||||
|
||||
// Check if they're at the same offset
|
||||
if (other.heapOffset == placed.heapOffset)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
@@ -49,8 +48,8 @@ internal struct ResourceBarrier
|
||||
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return AliasingPredecessor.IsValid
|
||||
? $"[Pass {PassIndex}] Aliasing Barrier: {AliasingPredecessor.Value}->{Resource.Value} Target: {TargetState.layout}"
|
||||
return AliasingPredecessor.IsValid
|
||||
? $"[Pass {PassIndex}] Aliasing Barrier: {AliasingPredecessor.Value}->{Resource.Value} Target: {TargetState.layout}"
|
||||
: $"[Pass {PassIndex}] Barrier: {Resource.Value} Target: {TargetState.layout}";
|
||||
}
|
||||
}
|
||||
@@ -87,8 +86,8 @@ internal struct CompiledBarrier
|
||||
|
||||
public override readonly string ToString()
|
||||
{
|
||||
return AliasingPredecessor.IsValid
|
||||
? $"[Pass {PassIndex}] Aliasing: {AliasingPredecessor.Value}->{Resource.Value} -> {TargetState.layout}"
|
||||
return AliasingPredecessor.IsValid
|
||||
? $"[Pass {PassIndex}] Aliasing: {AliasingPredecessor.Value}->{Resource.Value} -> {TargetState.layout}"
|
||||
: $"[Pass {PassIndex}] Transition: {Resource.Value} -> {TargetState.layout}";
|
||||
}
|
||||
}
|
||||
@@ -320,7 +319,7 @@ internal static class RenderGraphBarriers
|
||||
|
||||
var sync = BarrierSync.PixelShading | BarrierSync.NonPixelShading;
|
||||
var access = BarrierAccess.ShaderResource;
|
||||
|
||||
|
||||
var resource = resources.GetResource(handle);
|
||||
if (resource.bufferDesc.Usage.HasFlag(BufferUsage.IndirectArgument))
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ public interface IRenderGraphBuilder : IDisposable
|
||||
/// <param name="accessMode">The access mode specifying how the texture will be read or written during the pass.</param>
|
||||
/// <returns>An identifier for the texture.</returns>
|
||||
Identifier<RGTexture> UseTexture(Identifier<RGTexture> texture, AccessFlags accessMode);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified buffer for use in the current render graph pass with the given access mode.
|
||||
/// </summary>
|
||||
@@ -195,7 +195,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
||||
_resources.SetProducer(handle.AsResource(), _pass.index);
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
public Identifier<RGBuffer> CreateBuffer(in BufferDesc desc, string name)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
@@ -211,7 +211,7 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
||||
ThrowIfDisposed();
|
||||
return UseResource(texture.AsResource(), flags, RenderGraphResourceType.Texture).AsTexture();
|
||||
}
|
||||
|
||||
|
||||
public Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> buffer, AccessFlags flags)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
@@ -15,19 +15,19 @@ internal sealed class CachedCompilation
|
||||
|
||||
// Culling decisions for each pass
|
||||
public readonly List<bool> passCulledFlags = new(64);
|
||||
|
||||
|
||||
// Physical resource aliasing mappings (logical index -> physical index)
|
||||
public readonly Dictionary<int, int> logicalToPhysical = new(128);
|
||||
|
||||
|
||||
// Placed resource metadata
|
||||
public readonly List<PlacedResourceData> placedResources = new(32);
|
||||
|
||||
|
||||
// Compiled barriers (stores only target states, queries before state from ResourceManager)
|
||||
public readonly List<CompiledBarrier> compiledBarriers = new(128);
|
||||
|
||||
// Real gpu resource
|
||||
public readonly List<Handle<GPUResource>> backingResources = new(32);
|
||||
|
||||
|
||||
// View state used for this compilation
|
||||
public ViewState viewState;
|
||||
|
||||
@@ -94,18 +94,18 @@ internal sealed class RenderGraphCompilationCache
|
||||
{
|
||||
_cachedHash = hash;
|
||||
_hasCachedData = true;
|
||||
|
||||
|
||||
// Deep copy the data
|
||||
_cached.Clear();
|
||||
|
||||
|
||||
_cached.compiledPassIndices.AddRange(data.compiledPassIndices);
|
||||
_cached.passCulledFlags.AddRange(data.passCulledFlags);
|
||||
|
||||
|
||||
foreach (var kvp in data.logicalToPhysical)
|
||||
{
|
||||
_cached.logicalToPhysical[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
|
||||
_cached.placedResources.AddRange(data.placedResources);
|
||||
_cached.compiledBarriers.AddRange(data.compiledBarriers);
|
||||
|
||||
@@ -137,8 +137,8 @@ internal sealed class RenderGraphCompilationCache
|
||||
/// </summary>
|
||||
public (int hits, int misses, double hitRate) GetStatistics()
|
||||
{
|
||||
int total = CacheHits + CacheMisses;
|
||||
double hitRate = total > 0 ? (double)CacheHits / total : 0.0;
|
||||
var total = CacheHits + CacheMisses;
|
||||
var hitRate = total > 0 ? (double)CacheHits / total : 0.0;
|
||||
return (CacheHits, CacheMisses, hitRate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ internal sealed class RenderGraphCompiler
|
||||
/// <summary>
|
||||
/// Compiles the render graph by culling passes, allocating resources, and preparing barriers.
|
||||
/// </summary>
|
||||
public void Compile(
|
||||
public Error Compile(
|
||||
in ViewState viewState,
|
||||
ulong graphHash,
|
||||
List<RenderGraphPassBase> passes,
|
||||
@@ -43,6 +43,8 @@ internal sealed class RenderGraphCompiler
|
||||
List<NativeRenderPass> nativePasses,
|
||||
List<CompiledBarrier> compiledBarriers)
|
||||
{
|
||||
Error error;
|
||||
|
||||
// Try to restore from cache
|
||||
if (_compilationCache.TryGetCached(graphHash, out var cached))
|
||||
{
|
||||
@@ -54,7 +56,11 @@ internal sealed class RenderGraphCompiler
|
||||
RestoreFromCache(cached, compiledPasses, passes, nativePasses, compiledBarriers);
|
||||
|
||||
_aliasingManager.AssignPhysicalResources(_resources, passes.Count);
|
||||
AllocateResources();
|
||||
error = AllocateResources();
|
||||
if (error != Error.None)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
cached.viewState = viewState;
|
||||
}
|
||||
@@ -64,7 +70,7 @@ internal sealed class RenderGraphCompiler
|
||||
RestoreFromCache(cached, compiledPasses, passes, nativePasses, compiledBarriers);
|
||||
}
|
||||
|
||||
return;
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
// Fresh compilation needed
|
||||
@@ -87,16 +93,19 @@ internal sealed class RenderGraphCompiler
|
||||
}
|
||||
|
||||
_aliasingManager.AssignPhysicalResources(_resources, passes.Count);
|
||||
AllocateResources();
|
||||
error = AllocateResources();
|
||||
if (error != Error.None)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
CompileBarriers(compiledPasses, compiledBarriers);
|
||||
_nativePassBuilder.BuildNativeRenderPasses(compiledPasses, nativePasses, compiledBarriers);
|
||||
StoreInCache(graphHash, viewState, compiledPasses, passes, compiledBarriers);
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks passes that write to imported resources as having side effects.
|
||||
/// </summary>
|
||||
private void MarkPassesWithSideEffects(List<RenderGraphPassBase> passes)
|
||||
{
|
||||
for (var i = 0; i < passes.Count; i++)
|
||||
@@ -121,9 +130,6 @@ internal sealed class RenderGraphCompiler
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Culls unused passes based on dependency analysis.
|
||||
/// </summary>
|
||||
private void CullPasses(List<RenderGraphPassBase> passes)
|
||||
{
|
||||
// Mark all passes as culled initially
|
||||
@@ -143,9 +149,6 @@ internal sealed class RenderGraphCompiler
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively un-culls dependencies of a pass.
|
||||
/// </summary>
|
||||
private void UnculDependencies(RenderGraphPassBase pass, List<RenderGraphPassBase> passes)
|
||||
{
|
||||
// Un-cull producers of read resources
|
||||
@@ -180,9 +183,6 @@ internal sealed class RenderGraphCompiler
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Un-culls the producer of a resource.
|
||||
/// </summary>
|
||||
private void UnculProducer(Identifier<RGResource> resource, List<RenderGraphPassBase> passes)
|
||||
{
|
||||
var res = _resources.GetResource(resource);
|
||||
@@ -197,10 +197,7 @@ internal sealed class RenderGraphCompiler
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates GPU resources for the render graph.
|
||||
/// </summary>
|
||||
private void AllocateResources()
|
||||
private Error AllocateResources()
|
||||
{
|
||||
if (_resourceHeap.IsValid)
|
||||
{
|
||||
@@ -211,15 +208,15 @@ internal sealed class RenderGraphCompiler
|
||||
continue;
|
||||
}
|
||||
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
|
||||
_resourceManager.ResourceDatabase.ReleaseResource(res.backingResource);
|
||||
}
|
||||
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
|
||||
_resourceManager.ResourceDatabase.ReleaseResource(_resourceHeap);
|
||||
}
|
||||
|
||||
if (_aliasingManager.Heap.size == 0)
|
||||
{
|
||||
return;
|
||||
return Error.None; // No resources to allocate
|
||||
}
|
||||
|
||||
var allocationDesc = new AllocationDesc
|
||||
@@ -231,6 +228,10 @@ internal sealed class RenderGraphCompiler
|
||||
};
|
||||
|
||||
_resourceHeap = _resourceManager.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
|
||||
if (_resourceHeap.IsInvalid)
|
||||
{
|
||||
return Error.InvalidState;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _resources.Resources.Count; i++)
|
||||
{
|
||||
@@ -263,22 +264,22 @@ internal sealed class RenderGraphCompiler
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
if (res.backingResource.IsInvalid)
|
||||
{
|
||||
return Error.InvalidState;
|
||||
}
|
||||
|
||||
_compilationCache.UpdateBackingResource(i, res.backingResource);
|
||||
}
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles all barriers needed for execution.
|
||||
/// Delegates to RenderGraphBarriers for the actual compilation logic.
|
||||
/// </summary>
|
||||
private void CompileBarriers(List<RenderGraphPassBase> compiledPasses, List<CompiledBarrier> compiledBarriers)
|
||||
{
|
||||
RenderGraphBarriers.CompileBarriers(compiledPasses, compiledBarriers, _resources, _aliasingManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores the render graph state from cached compilation results.
|
||||
/// </summary>
|
||||
private void RestoreFromCache(
|
||||
CachedCompilation cached,
|
||||
List<RenderGraphPassBase> compiledPasses,
|
||||
@@ -323,9 +324,6 @@ internal sealed class RenderGraphCompiler
|
||||
_nativePassBuilder.BuildNativeRenderPasses(compiledPasses, nativePasses, compiledBarriers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores current compilation results in the cache.
|
||||
/// </summary>
|
||||
private void StoreInCache(
|
||||
ulong graphHash,
|
||||
in ViewState viewState,
|
||||
@@ -368,9 +366,6 @@ internal sealed class RenderGraphCompiler
|
||||
_compilationCache.Store(graphHash, cacheData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases allocated GPU resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_resourceHeap.IsValid)
|
||||
@@ -379,11 +374,11 @@ internal sealed class RenderGraphCompiler
|
||||
{
|
||||
if (!res.isImported)
|
||||
{
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
|
||||
_resourceManager.ResourceDatabase.ReleaseResource(res.backingResource);
|
||||
}
|
||||
}
|
||||
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
|
||||
_resourceManager.ResourceDatabase.ReleaseResource(_resourceHeap);
|
||||
_resourceHeap = Handle<GPUResource>.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
private int _activeMeshIndexCount;
|
||||
|
||||
public IResourceManager ResourceManager => _resourceManager;
|
||||
|
||||
|
||||
public int ActiveMeshIndexCount => _activeMeshIndexCount;
|
||||
|
||||
public ICommandBuffer CommandBuffer => _commandBuffer;
|
||||
@@ -76,7 +76,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
internal void SetRenderTargetFormats(ReadOnlySpan<TextureFormat> rtvFormats, TextureFormat dsvFormat)
|
||||
{
|
||||
for (int i = 0; i < RHIUtility.MAX_RENDER_TARGETS; i++)
|
||||
for (var i = 0; i < RHIUtility.MAX_RENDER_TARGETS; i++)
|
||||
{
|
||||
_rtvFormats[i] = i < rtvFormats.Length ? rtvFormats[i] : TextureFormat.Unknown;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ internal sealed class RenderGraphExecutor
|
||||
/// <summary>
|
||||
/// Executes all compiled passes using native render passes where possible.
|
||||
/// </summary>
|
||||
public unsafe void Execute(
|
||||
public unsafe Error Execute(
|
||||
ICommandBuffer cmd,
|
||||
List<RenderGraphPassBase> compiledPasses,
|
||||
List<NativeRenderPass> nativePasses,
|
||||
@@ -53,7 +53,11 @@ internal sealed class RenderGraphExecutor
|
||||
for (var i = 0; i < nativePass.mergedPassIndices.Count; i++)
|
||||
{
|
||||
var mergedPassIdx = nativePass.mergedPassIndices[i];
|
||||
ExecuteBarriersForPass(cmd, mergedPassIdx, ref barrierIndex, compiledBarriers);
|
||||
var e = ExecuteBarriersForPass(cmd, mergedPassIdx, ref barrierIndex, compiledBarriers);
|
||||
if (e != Error.None)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
// Begin native render pass
|
||||
@@ -119,19 +123,25 @@ internal sealed class RenderGraphExecutor
|
||||
else
|
||||
{
|
||||
// Compute pass or standalone raster pass (not merged) or Unsafe pass
|
||||
ExecuteBarriersForPass(cmd, logicalPassIndex, ref barrierIndex, compiledBarriers);
|
||||
var e = ExecuteBarriersForPass(cmd, logicalPassIndex, ref barrierIndex, compiledBarriers);
|
||||
if (e != Error.None)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
pass.Execute(_context);
|
||||
logicalPassIndex++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all barriers for a specific pass.
|
||||
/// Uses pre-compiled barriers and queries before state from ResourceManager.
|
||||
/// </summary>
|
||||
private unsafe void ExecuteBarriersForPass(
|
||||
private unsafe Error ExecuteBarriersForPass(
|
||||
ICommandBuffer cmd,
|
||||
int passIndex,
|
||||
ref int barrierIndex,
|
||||
@@ -158,7 +168,13 @@ internal sealed class RenderGraphExecutor
|
||||
var resourceHandle = resource.backingResource;
|
||||
|
||||
// Always query the before state from ResourceManager (single source of truth)
|
||||
var currentState = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow();
|
||||
var currentStateResult = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle);
|
||||
if (currentStateResult.IsFailure)
|
||||
{
|
||||
return currentStateResult.Error;
|
||||
}
|
||||
|
||||
var currentState = currentStateResult.Value;
|
||||
|
||||
BarrierLayout layoutBefore;
|
||||
BarrierAccess accessBefore;
|
||||
@@ -168,7 +184,13 @@ internal sealed class RenderGraphExecutor
|
||||
if (compiledBarrier.AliasingPredecessor.IsValid)
|
||||
{
|
||||
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
|
||||
var predState = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow();
|
||||
var predStateResult = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle);
|
||||
if (predStateResult.IsFailure)
|
||||
{
|
||||
return predStateResult.Error;
|
||||
}
|
||||
|
||||
var predState = predStateResult.Value;
|
||||
|
||||
layoutBefore = BarrierLayout.Undefined;
|
||||
accessBefore = BarrierAccess.NoAccess;
|
||||
@@ -184,9 +206,9 @@ internal sealed class RenderGraphExecutor
|
||||
var target = compiledBarrier.TargetState;
|
||||
|
||||
// Skip if already in target state (optimization)
|
||||
if (!compiledBarrier.AliasingPredecessor.IsValid &&
|
||||
layoutBefore == target.layout &&
|
||||
accessBefore == target.access &&
|
||||
if (!compiledBarrier.AliasingPredecessor.IsValid &&
|
||||
layoutBefore == target.layout &&
|
||||
accessBefore == target.access &&
|
||||
syncBefore == target.sync)
|
||||
{
|
||||
continue;
|
||||
@@ -218,5 +240,7 @@ internal sealed class RenderGraphExecutor
|
||||
}
|
||||
|
||||
Flush();
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,43 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Core.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.IO.Hashing;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
/// Computes structural hashes of render graphs for compilation caching.
|
||||
/// Hashes are based on graph topology and resource configurations, not runtime values.
|
||||
/// </summary>
|
||||
internal static class RenderGraphHasher
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes a hash of the entire render graph structure.
|
||||
/// Used for cache invalidation - same hash means same compilation result.
|
||||
/// </summary>
|
||||
public static unsafe ulong ComputeGraphHash(List<RenderGraphPassBase> passes, RenderGraphResourceRegistry resources)
|
||||
public static ulong ComputeGraphHash(List<RenderGraphPassBase> passes, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
using var scope = AllocationManager.CreateStackScope();
|
||||
var bufferPool = new UnsafeList<byte>(2048, scope.AllocationHandle);
|
||||
var pData = (byte*)bufferPool.GetUnsafePtr();
|
||||
var offset = 0;
|
||||
var writer = new BufferWriter(2048, scope.AllocationHandle);
|
||||
|
||||
// Hash pass count
|
||||
*(int*)(pData + offset) = passes.Count;
|
||||
offset += sizeof(int);
|
||||
writer.Write(passes.Count);
|
||||
|
||||
// Hash each pass structure (excluding names)
|
||||
for (var i = 0; i < passes.Count; i++)
|
||||
{
|
||||
var pass = passes[i];
|
||||
|
||||
*(RenderPassType*)(pData + offset) = pass.type;
|
||||
offset += sizeof(RenderPassType);
|
||||
|
||||
*(bool*)(pData + offset) = pass.allowCulling;
|
||||
offset += sizeof(bool);
|
||||
|
||||
*(bool*)(pData + offset) = pass.asyncCompute;
|
||||
offset += sizeof(bool);
|
||||
writer.Write(pass.type);
|
||||
writer.Write(pass.allowCulling);
|
||||
writer.Write(pass.asyncCompute);
|
||||
|
||||
// Hash depth attachment
|
||||
offset = ComputeTextureHash(pData, offset, pass.depthAccess.id, resources);
|
||||
ComputeTextureHash(ref writer, pass.depthAccess.id, resources);
|
||||
|
||||
pData[offset] = (byte)pass.depthAccess.accessFlags;
|
||||
offset += sizeof(AccessFlags);
|
||||
writer.Write(pass.depthAccess.accessFlags);
|
||||
writer.Write(pass.maxColorIndex);
|
||||
|
||||
*(int*)(pData + offset) = pass.maxColorIndex;
|
||||
offset += sizeof(int);
|
||||
for (var j = 0; j <= pass.maxColorIndex; j++)
|
||||
{
|
||||
offset = ComputeTextureHash(pData, offset, pass.colorAccess[j].id, resources);
|
||||
|
||||
pData[offset] = (byte)pass.colorAccess[j].accessFlags;
|
||||
offset += sizeof(AccessFlags);
|
||||
ComputeTextureHash(ref writer, pass.colorAccess[j].id, resources);
|
||||
writer.Write(pass.colorAccess[j].accessFlags);
|
||||
}
|
||||
|
||||
for (var j = 0; j < (int)RenderGraphResourceType.Count; j++)
|
||||
@@ -64,45 +46,35 @@ internal static class RenderGraphHasher
|
||||
var writeList = pass.resourceWrites[j];
|
||||
var createList = pass.resourceCreates[j];
|
||||
|
||||
*(int*)(pData + offset) = readList.Count;
|
||||
offset += sizeof(int);
|
||||
writer.Write(readList.Count);
|
||||
for (var k = 0; k < readList.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = readList[k].Value;
|
||||
offset += sizeof(int);
|
||||
writer.Write(readList[k].Value);
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = writeList.Count;
|
||||
offset += sizeof(int);
|
||||
writer.Write(writeList.Count);
|
||||
for (var k = 0; k < writeList.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = writeList[k].Value;
|
||||
offset += sizeof(int);
|
||||
writer.Write(writeList[k].Value);
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = createList.Count;
|
||||
offset += sizeof(int);
|
||||
writer.Write(createList.Count);
|
||||
for (var k = 0; k < createList.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = createList[k].Value;
|
||||
offset += sizeof(int);
|
||||
writer.Write(createList[k].Value);
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = pass.randomAccess.Count;
|
||||
offset += sizeof(int);
|
||||
writer.Write(pass.randomAccess.Count);
|
||||
for (var k = 0; k < pass.randomAccess.Count; k++)
|
||||
{
|
||||
*(int*)(pData + offset) = pass.randomAccess[k].Value;
|
||||
offset += sizeof(int);
|
||||
writer.Write(pass.randomAccess[k].Value);
|
||||
}
|
||||
}
|
||||
|
||||
*(int*)(pData + offset) = pass.GetRenderFuncHashCode();
|
||||
offset += sizeof(int);
|
||||
writer.Write(pass.GetRenderFuncHashCode());
|
||||
}
|
||||
|
||||
var span = new Span<byte>(pData, offset);
|
||||
return XxHash64.HashToUInt64(span);
|
||||
return XxHash64.HashToUInt64(writer.AsSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,68 +82,49 @@ internal static class RenderGraphHasher
|
||||
/// For imported textures, hashes the backing handle.
|
||||
/// For transient textures, hashes the descriptor (respecting size mode).
|
||||
/// </summary>
|
||||
private static unsafe int ComputeTextureHash(byte* pData, int offset, Identifier<RGTexture> texture, RenderGraphResourceRegistry resources)
|
||||
private static void ComputeTextureHash(ref BufferWriter writer, Identifier<RGTexture> texture, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
if (texture.IsInvalid)
|
||||
{
|
||||
return offset;
|
||||
return;
|
||||
}
|
||||
|
||||
var resource = resources.GetResource(texture.AsResource());
|
||||
|
||||
// Hash imported flag
|
||||
*(pData + offset) = resource.isImported ? (byte)1 : (byte)0;
|
||||
offset += sizeof(byte);
|
||||
writer.Write(resource.isImported);
|
||||
|
||||
// For imported textures, hash the backing resource handle
|
||||
if (resource.isImported)
|
||||
{
|
||||
*(int*)(pData + offset) = resource.backingResource.GetHashCode();
|
||||
offset += sizeof(int);
|
||||
return offset;
|
||||
writer.Write(resource.backingResource.GetHashCode());
|
||||
return;
|
||||
}
|
||||
|
||||
var desc = resource.rgTextureDesc;
|
||||
|
||||
// Hash format (structural)
|
||||
*(TextureFormat*)(pData + offset) = desc.format;
|
||||
offset += sizeof(TextureFormat);
|
||||
|
||||
// Hash size mode (structural)
|
||||
*(RGTextureSizeMode*)(pData + offset) = desc.sizeMode;
|
||||
offset += sizeof(RGTextureSizeMode);
|
||||
writer.Write(desc.format);
|
||||
writer.Write(desc.sizeMode);
|
||||
|
||||
// Hash size specification based on mode
|
||||
if (desc.sizeMode == RGTextureSizeMode.Absolute)
|
||||
{
|
||||
// Absolute mode: hash actual dimensions
|
||||
*(uint*)(pData + offset) = desc.width;
|
||||
offset += sizeof(uint);
|
||||
*(uint*)(pData + offset) = desc.height;
|
||||
offset += sizeof(uint);
|
||||
writer.Write(desc.width);
|
||||
writer.Write(desc.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Relative mode: hash scale factors (NOT resolved dimensions)
|
||||
*(float*)(pData + offset) = desc.scaleX;
|
||||
offset += sizeof(float);
|
||||
*(float*)(pData + offset) = desc.scaleY;
|
||||
offset += sizeof(float);
|
||||
writer.Write(desc.scaleX);
|
||||
writer.Write(desc.scaleY);
|
||||
}
|
||||
|
||||
// Hash other structural properties
|
||||
*(TextureDimension*)(pData + offset) = desc.dimension;
|
||||
offset += sizeof(TextureDimension);
|
||||
*(uint*)(pData + offset) = desc.mipLevels;
|
||||
offset += sizeof(uint);
|
||||
*(TextureUsage*)(pData + offset) = desc.usage;
|
||||
offset += sizeof(TextureUsage);
|
||||
|
||||
*(bool*)(pData + offset) = desc.clearAtFirstUse;
|
||||
offset += sizeof(bool);
|
||||
*(bool*)(pData + offset) = desc.discardAtLastUse;
|
||||
offset += sizeof(bool);
|
||||
|
||||
return offset;
|
||||
writer.Write(desc.dimension);
|
||||
writer.Write(desc.mipLevels);
|
||||
writer.Write(desc.usage);
|
||||
writer.Write(desc.clearAtFirstUse);
|
||||
writer.Write(desc.discardAtLastUse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
/// <summary>
|
||||
@@ -9,35 +7,35 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
internal sealed class NativeRenderPass
|
||||
{
|
||||
public int index;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indices of logical passes merged into this native render pass.
|
||||
/// </summary>
|
||||
public readonly List<int> mergedPassIndices = new(4);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Color attachments shared across all merged passes.
|
||||
/// </summary>
|
||||
public RenderTargetInfo[] colorAttachments = new RenderTargetInfo[8];
|
||||
public int colorAttachmentCount;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Depth-stencil attachment (optional).
|
||||
/// </summary>
|
||||
public DepthStencilInfo depthAttachment;
|
||||
public bool hasDepthAttachment;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Range of logical passes included in this native pass.
|
||||
/// </summary>
|
||||
public int firstLogicalPass;
|
||||
public int lastLogicalPass;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Whether UAV writes are allowed during this render pass.
|
||||
/// </summary>
|
||||
public bool allowUAVWrites;
|
||||
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
index = -1;
|
||||
|
||||
@@ -44,7 +44,7 @@ internal abstract class RenderGraphPassBase
|
||||
|
||||
public RenderGraphPassBase()
|
||||
{
|
||||
for (int i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||
for (var i = 0; i < (int)RenderGraphResourceType.Count; i++)
|
||||
{
|
||||
resourceReads[i] = new List<Identifier<RGResource>>(8);
|
||||
resourceWrites[i] = new List<Identifier<RGResource>>(4);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Buffer;
|
||||
|
||||
@@ -85,15 +84,15 @@ internal sealed class RenderGraphResource
|
||||
|
||||
public int index;
|
||||
public RenderGraphResourceType type;
|
||||
|
||||
|
||||
// Resource descriptors (only one is valid based on type)
|
||||
public RGTextureDesc rgTextureDesc;
|
||||
public BufferDesc bufferDesc;
|
||||
|
||||
|
||||
// Resolved dimensions (computed from rgTextureDesc + ViewState for textures)
|
||||
public uint resolvedWidth;
|
||||
public uint resolvedHeight;
|
||||
|
||||
|
||||
public bool isImported;
|
||||
public int firstUsePass = -1;
|
||||
public int lastUsePass = -1;
|
||||
@@ -145,8 +144,8 @@ internal sealed class RenderGraphResourceRegistry
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < _resources.Count; i++)
|
||||
var count = 0;
|
||||
for (var i = 0; i < _resources.Count; i++)
|
||||
{
|
||||
if (_resources[i].type == RenderGraphResourceType.Texture)
|
||||
count++;
|
||||
@@ -158,8 +157,8 @@ internal sealed class RenderGraphResourceRegistry
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < _resources.Count; i++)
|
||||
var count = 0;
|
||||
for (var i = 0; i < _resources.Count; i++)
|
||||
{
|
||||
if (_resources[i].type == RenderGraphResourceType.Buffer)
|
||||
count++;
|
||||
@@ -226,7 +225,7 @@ internal sealed class RenderGraphResourceRegistry
|
||||
|
||||
return new Identifier<RGTexture>(resource.index);
|
||||
}
|
||||
|
||||
|
||||
public Identifier<RGBuffer> ImportBuffer(ref readonly BufferDesc desc, Handle<GraphicsBuffer> buffer, string name)
|
||||
{
|
||||
var resource = _pool.Rent<RenderGraphResource>();
|
||||
@@ -245,7 +244,7 @@ internal sealed class RenderGraphResourceRegistry
|
||||
public Identifier<RGBuffer> CreateBuffer(ref readonly BufferDesc desc, string name)
|
||||
{
|
||||
var resource = _pool.Rent<RenderGraphResource>();
|
||||
resource.name= name;
|
||||
resource.name = name;
|
||||
resource.type = RenderGraphResourceType.Buffer;
|
||||
resource.index = _resources.Count;
|
||||
resource.bufferDesc = desc;
|
||||
@@ -260,17 +259,17 @@ internal sealed class RenderGraphResourceRegistry
|
||||
{
|
||||
return _resources[resource.Value];
|
||||
}
|
||||
|
||||
|
||||
public RenderGraphResource GetResource(Identifier<RGTexture> texture)
|
||||
{
|
||||
return _resources[texture.Value];
|
||||
}
|
||||
|
||||
|
||||
public RenderGraphResource GetResource(Identifier<RGBuffer> buffer)
|
||||
{
|
||||
return _resources[buffer.Value];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets resource by global index. Use this when iterating over all resources.
|
||||
/// </summary>
|
||||
@@ -299,7 +298,7 @@ internal sealed class RenderGraphResourceRegistry
|
||||
resource.firstUsePass = passIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Resolves texture sizes based on current view state.
|
||||
/// Must be called after all resources are created and before compilation.
|
||||
@@ -311,7 +310,7 @@ internal sealed class RenderGraphResourceRegistry
|
||||
var res = _resources[i];
|
||||
if (res.type != RenderGraphResourceType.Texture || res.isImported)
|
||||
continue;
|
||||
|
||||
|
||||
var desc = res.rgTextureDesc;
|
||||
if (desc.sizeMode == RGTextureSizeMode.Absolute)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -22,7 +21,7 @@ public enum RGTextureSizeMode : byte
|
||||
/// Fixed pixel dimensions (width, height).
|
||||
/// </summary>
|
||||
Absolute,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Scale relative to view state (scaleX * viewportWidth, scaleY * viewportHeight).
|
||||
/// </summary>
|
||||
@@ -40,7 +39,7 @@ public struct ViewState : IEquatable<ViewState>
|
||||
// For upscalers that need to know the original render target size before upscaling
|
||||
public uint actualWidth;
|
||||
public uint actualHeight;
|
||||
|
||||
|
||||
public ViewState(uint width, uint height, uint actualWidth, uint actualHeight)
|
||||
{
|
||||
viewportWidth = width;
|
||||
@@ -48,28 +47,28 @@ public struct ViewState : IEquatable<ViewState>
|
||||
this.actualWidth = actualWidth;
|
||||
this.actualHeight = actualHeight;
|
||||
}
|
||||
|
||||
|
||||
public readonly bool Equals(ViewState other)
|
||||
{
|
||||
return viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight
|
||||
&& actualWidth == other.actualWidth && actualHeight == other.actualHeight;
|
||||
}
|
||||
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is ViewState other && Equals(other);
|
||||
}
|
||||
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(viewportWidth, viewportHeight);
|
||||
}
|
||||
|
||||
|
||||
public static bool operator ==(ViewState left, ViewState right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
|
||||
public static bool operator !=(ViewState left, ViewState right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
@@ -82,29 +81,29 @@ public struct ViewState : IEquatable<ViewState>
|
||||
public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
{
|
||||
public RGTextureSizeMode sizeMode;
|
||||
|
||||
|
||||
// Size specification (union-like - only one set is used based on sizeMode)
|
||||
public uint width; // For Absolute mode
|
||||
public uint height; // For Absolute mode
|
||||
public float scaleX; // For Relative mode
|
||||
public float scaleY; // For Relative mode
|
||||
|
||||
|
||||
// Common texture properties
|
||||
public TextureFormat format;
|
||||
public TextureDimension dimension;
|
||||
public uint mipLevels;
|
||||
public uint slice;
|
||||
public TextureUsage usage;
|
||||
|
||||
|
||||
public bool clearAtFirstUse;
|
||||
public bool discardAtLastUse;
|
||||
|
||||
|
||||
// Clear operation support
|
||||
public Color128 clearColor;
|
||||
|
||||
public float clearDepth;
|
||||
public byte clearStencil;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture descriptor with absolute dimensions.
|
||||
/// </summary>
|
||||
@@ -137,7 +136,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
usage = usage
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture descriptor with relative dimensions (uniform scale).
|
||||
/// </summary>
|
||||
@@ -169,7 +168,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
usage = usage
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture descriptor with relative dimensions (non-uniform scale).
|
||||
/// </summary>
|
||||
@@ -203,7 +202,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a depth texture descriptor with relative dimensions.
|
||||
/// </summary>
|
||||
@@ -233,7 +232,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts to RHI TextureDesc using resolved dimensions.
|
||||
/// </summary>
|
||||
@@ -250,7 +249,7 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
Usage = usage
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public readonly bool Equals(RGTextureDesc other)
|
||||
{
|
||||
return sizeMode == other.sizeMode &&
|
||||
@@ -266,12 +265,12 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
: scaleX == other.scaleX && scaleY == other.scaleY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is RGTextureDesc other && Equals(other);
|
||||
}
|
||||
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
if (sizeMode == RGTextureSizeMode.Absolute)
|
||||
@@ -283,12 +282,12 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
return HashCode.Combine(sizeMode, scaleX, scaleY, format, dimension, mipLevels, slice, usage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static bool operator ==(RGTextureDesc left, RGTextureDesc right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
|
||||
public static bool operator !=(RGTextureDesc left, RGTextureDesc right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
|
||||
@@ -107,8 +107,8 @@ internal class MeshRenderPass : IRenderPass
|
||||
private void CompileBlitShader(ref readonly RenderingContext ctx)
|
||||
{
|
||||
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Blit.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
||||
_blitShader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
|
||||
_blitMaterial = ctx.ResourceAllocator.CreateMaterial(_blitShader);
|
||||
_blitShader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
|
||||
_blitMaterial = ctx.ResourceManager.CreateMaterial(_blitShader);
|
||||
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
@@ -132,8 +132,8 @@ internal class MeshRenderPass : IRenderPass
|
||||
|
||||
var shaderDescriptor = DSLShaderCompiler.CompileShader("F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/test.gshdr", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow();
|
||||
|
||||
_shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor);
|
||||
_material = ctx.ResourceAllocator.CreateMaterial(_shader);
|
||||
_shader = ctx.ResourceManager.CreateGraphicsShader(shaderDescriptor);
|
||||
_material = ctx.ResourceManager.CreateMaterial(_shader);
|
||||
|
||||
for (var i = 0; i < shaderDescriptor.passes.Length; i++)
|
||||
{
|
||||
@@ -320,7 +320,7 @@ internal class MeshRenderPass : IRenderPass
|
||||
{
|
||||
foreach (var texture in _textures)
|
||||
{
|
||||
resourceManager.ResourceDatabase.ScheduleReleaseResource(texture.AsResource());
|
||||
resourceManager.ResourceDatabase.ReleaseResource(texture.AsResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,14 +222,13 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
if (!_meshes.TryGetElementAt(handle.ID, handle.Generation, out var mesh))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseResource(mesh);
|
||||
_meshes.Remove(handle.ID, handle.Generation);
|
||||
mesh.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
public bool HasMaterial(Handle<Material> handle)
|
||||
@@ -253,14 +252,14 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
var material = _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseResource(material);
|
||||
_materials.Remove(handle.ID, handle.Generation);
|
||||
material.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
@@ -288,14 +287,8 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
ref var shader = ref _shaders[id.Value]!;
|
||||
ReleaseResource(shader);
|
||||
}
|
||||
|
||||
private void ReleaseResource<T>(T resource)
|
||||
where T : IResourceReleasable
|
||||
{
|
||||
resource.ReleaseResource(_resourceDatabase);
|
||||
var shader = _shaders[id.Value];
|
||||
shader.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -307,17 +300,17 @@ internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
|
||||
foreach (var mesh in _meshes)
|
||||
{
|
||||
ReleaseResource(mesh);
|
||||
mesh.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
foreach (var material in _materials)
|
||||
{
|
||||
ReleaseResource(material);
|
||||
material.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
foreach (var shader in _shaders)
|
||||
{
|
||||
ReleaseResource(shader);
|
||||
shader.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
_meshes.Dispose();
|
||||
|
||||
Reference in New Issue
Block a user