From 4173ff24320e1def0c4fa2520598f79645168d2b Mon Sep 17 00:00:00 2001 From: Misaki Date: Thu, 22 Jan 2026 20:51:58 +0900 Subject: [PATCH] Refactor RenderGraph barrier/state tracking system Major overhaul of resource barrier and state tracking in RenderGraph: - Introduce ResourceBarrierData for explicit (layout, access, sync) tracking. - Separate aliasing and transition barriers; explicit aliasing support. - Remove BufferHint; infer buffer usage from BufferUsage flags. - Update TextureAccess/BufferAccess to include usage requirements. - Improve enums (BarrierSync, BarrierAccess, BarrierLayout) for D3D12 alignment. - Update D3D12CommandBuffer to use new barrier data and error handling. - Make D3D12DescriptorHeap a class; add ReleaseSampler to IResourceDatabase. - Reset resource pools and aliasing managers each frame. - Batch and flush barriers efficiently per pass. - Update HLSL mesh shader macros to [NumThreads]. - Remove obsolete code and improve documentation. This refactor improves correctness, extensibility, and prepares for advanced features. --- Ghost.Graphics/D3D12/D3D12CommandBuffer.cs | 24 +- Ghost.Graphics/D3D12/D3D12DescriptorHeap.cs | 23 +- Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs | 9 + Ghost.Graphics/D3D12/D3D12SwapChain.cs | 3 +- Ghost.Graphics/RHI/Common.cs | 73 +-- Ghost.Graphics/RHI/IResourceDatabase.cs | 6 + .../RenderGraphModule/RenderGraph.cs | 489 ++++++++---------- .../RenderGraphModule/RenderGraphAliasing.cs | 2 +- .../RenderGraphModule/RenderGraphBarriers.cs | 51 +- .../RenderGraphModule/RenderGraphBuilder.cs | 24 +- .../RenderGraphCompilationCache.cs | 2 +- .../RenderGraphModule/RenderGraphPass.cs | 5 - .../RenderGraphResourcePool.cs | 23 +- .../RenderGraphModule/RenderGraphTypes.cs | 133 +---- Ghost.Graphics/RenderPasses/MeshRenderPass.cs | 10 +- Ghost.Graphics/RenderPasses/ShaderCode.hlsl | 2 +- Ghost.Graphics/Shaders/Blit.gsdef | 3 +- Ghost.Graphics/Shaders/Includes/Common.hlsl | 1 - 18 files changed, 395 insertions(+), 488 deletions(-) diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs index 4b50e9a..cf11def 100644 --- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs +++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs @@ -248,7 +248,15 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer break; case BarrierType.Buffer: { - var resource = _resourceDatabase.GetResource(desc.Resource); + var r = _resourceDatabase.GetResourceRecord(desc.Resource); + if (r.IsFailure) + { + RecordError(nameof(ResourceBarrier), r.Error); + continue; + } + + ref var record = ref r.Value; + var resource = record.ResourcePtr; pBufferBarriers[bufferIndex++] = new D3D12_BUFFER_BARRIER { SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore, @@ -259,11 +267,21 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer Offset = 0, Size = ulong.MaxValue }; + + record.barrierData = new ResourceBarrierData(BarrierLayout.Undefined, desc.AccessAfter, desc.SyncAfter); } break; case BarrierType.Texture: { - var resource = _resourceDatabase.GetResource(desc.Resource); + var r = _resourceDatabase.GetResourceRecord(desc.Resource); + if (r.IsFailure) + { + RecordError(nameof(ResourceBarrier), r.Error); + continue; + } + + ref var record = ref r.Value; + var resource = record.ResourcePtr; pTextureBarriers[textureIndex++] = new D3D12_TEXTURE_BARRIER { SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore, @@ -282,6 +300,8 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer }, Flags = desc.Discard ? D3D12_TEXTURE_BARRIER_FLAGS.D3D12_TEXTURE_BARRIER_FLAG_DISCARD : D3D12_TEXTURE_BARRIER_FLAGS.D3D12_TEXTURE_BARRIER_FLAG_NONE }; + + record.barrierData = new ResourceBarrierData(desc.LayoutAfter, desc.AccessAfter, desc.SyncAfter); } break; } diff --git a/Ghost.Graphics/D3D12/D3D12DescriptorHeap.cs b/Ghost.Graphics/D3D12/D3D12DescriptorHeap.cs index ea03c26..0d0fd50 100644 --- a/Ghost.Graphics/D3D12/D3D12DescriptorHeap.cs +++ b/Ghost.Graphics/D3D12/D3D12DescriptorHeap.cs @@ -10,7 +10,7 @@ using static TerraFX.Aliases.D3D12_Alias; namespace Ghost.Graphics.D3D12; -internal unsafe struct D3D12DescriptorHeap : IDisposable +internal unsafe class D3D12DescriptorHeap : IDisposable { private const int _INVALID_DESCRIPTOR_INDEX = -1; @@ -51,8 +51,8 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable get; } - public readonly ID3D12DescriptorHeap* Heap => _heap.Get(); - public readonly ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get(); + public ID3D12DescriptorHeap* Heap => _heap.Get(); + public ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get(); public D3D12DescriptorHeap(string name, D3D12RenderDevice device, D3D12_DESCRIPTOR_HEAP_TYPE type, int numDescriptors) { @@ -164,17 +164,18 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable } } - public readonly D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(int index) + public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(int index) { if (index < 0 || index >= NumDescriptors) { throw new ArgumentOutOfRangeException(nameof(index), "Descriptor index is out of range."); } - return _startCpuHandle.Offset(index, Stride); + var handle = _startCpuHandle; + return handle.Offset(index, Stride); } - public readonly D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(int index) + public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(int index) { if (index < 0 || index >= NumDescriptors) { @@ -186,10 +187,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable throw new InvalidOperationException("Descriptor heap is not shader visible."); } - return _startCpuHandleShaderVisible.Offset(index, Stride); + var handle = _startCpuHandleShaderVisible; + return handle.Offset(index, Stride); } - public readonly D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(int index) + public D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(int index) { if (index < 0 || index >= NumDescriptors) { @@ -201,10 +203,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable throw new InvalidOperationException("Descriptor heap is not shader visible."); } - return _startGpuHandleShaderVisible.Offset(index, Stride); + var handle = _startGpuHandleShaderVisible; + return handle.Offset(index, Stride); } - public readonly void CopyToShaderVisibleHeap(int index, int count = 1) + public void CopyToShaderVisibleHeap(int index, int count = 1) { _device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType); } diff --git a/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs b/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs index c985e81..45de52a 100644 --- a/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs +++ b/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs @@ -323,6 +323,15 @@ internal class D3D12ResourceDatabase : IResourceDatabase return _samplers.TryGetValue(desc, out id); } + public void ReleaseSampler(Identifier id) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // NOTE: We almost never release samplers individually, because they are cheap and can be reused. + // Ideally we would release all samplers at once when disposing the ResourceDatabase. + _descriptorAllocator.Release(new Identifier(id.Value)); + } + public Handle AddMesh(ref readonly Mesh mesh) { ObjectDisposedException.ThrowIf(_disposed, this); diff --git a/Ghost.Graphics/D3D12/D3D12SwapChain.cs b/Ghost.Graphics/D3D12/D3D12SwapChain.cs index 4a43980..c6c956a 100644 --- a/Ghost.Graphics/D3D12/D3D12SwapChain.cs +++ b/Ghost.Graphics/D3D12/D3D12SwapChain.cs @@ -149,7 +149,8 @@ internal unsafe class D3D12SwapChain : ISwapChain pBackBuffer->SetName($"SwapChain_BackBuffer_{i}"); var rtv = _descriptorAllocator.AllocateRTV(); - _renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, _descriptorAllocator.GetCpuHandle(rtv)); + var cpuHandle = _descriptorAllocator.GetCpuHandle(rtv); + _renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, cpuHandle); var view = ResourceViewGroup.Invalid with { diff --git a/Ghost.Graphics/RHI/Common.cs b/Ghost.Graphics/RHI/Common.cs index abb53de..000a72c 100644 --- a/Ghost.Graphics/RHI/Common.cs +++ b/Ghost.Graphics/RHI/Common.cs @@ -837,36 +837,36 @@ public enum BarrierType } [Flags] -public enum BarrierSync : uint +public enum BarrierSync { None = 0x0, - All = 0x0, - Draw = 0x1, - IndexInput = 0x2, - VertexShading = 0x4, - PixelShading = 0x8, - DepthStencil = 0x10, - RenderTarget = 0x20, - ComputeShading = 0x40, - Raytracing = 0x80, - Copy = 0x100, - Resolve = 0x200, - ExecuteIndirect = 0x400, + All = 0x1, + Draw = 0x2, + IndexInput = 0x4, + VertexShading = 0x8, + PixelShading = 0x10, + DepthStencil = 0x20, + RenderTarget = 0x40, + ComputeShading = 0x80, + Raytracing = 0x100, + Copy = 0x200, + Resolve = 0x400, + ExecuteIndirect = 0x800, Predication = 0x800, - AllShading = VertexShading | PixelShading | ComputeShading | Raytracing, - NonPixelShading = VertexShading | ComputeShading | Raytracing, - EmitRaytracingAccelerationStructurePostbuildInfo = 0x1000, - ClearUnorderedAccessView = 0x2000, - VideoDecode = 0x40000, - VideoProcess = 0x80000, - VideoEncode = 0x100000, - BuildRaytracingAccelerationStructure = 0x200000, - CopyRaytracingAccelerationStructure = 0x400000, - Split = 0x800000 + AllShading = 0x1000, + NonPixelShading = 0x2000, + EmitRaytracingAccelerationStructurePostbuildInfo = 0x4000, + ClearUnorderedAccessView = 0x8000, + VideoDecode = 0x100000, + VideoProcess = 0x200000, + VideoEncode = 0x400000, + BuildRaytracingAccelerationStructure = 0x800000, + CopyRaytracingAccelerationStructure = 0x1000000, + Split = unchecked((int)0x80000000) } [Flags] -public enum BarrierAccess : uint +public enum BarrierAccess { Common = 0, VertexBuffer = 0x1, @@ -879,6 +879,7 @@ public enum BarrierAccess : uint ShaderResource = 0x80, StreamOutput = 0x100, IndirectArgument = 0x200, + Predication = 0x200, CopyDest = 0x400, CopySource = 0x800, ResolveDest = 0x1000, @@ -892,7 +893,7 @@ public enum BarrierAccess : uint VideoProcessWrite = 0x100000, VideoEncodeRead = 0x200000, VideoEncodeWrite = 0x400000, - NoAccess = 0x80000000 + NoAccess = unchecked((int)0x80000000) } public enum BarrierLayout @@ -906,10 +907,10 @@ public enum BarrierLayout DepthStencilWrite = 4, DepthStencilRead = 5, ShaderResource = 6, - CopyDest = 7, - CopySource = 8, - ResolveDest = 9, - ResolveSource = 10, + CopySource = 7, + CopyDest = 8, + ResolveSource = 9, + ResolveDest = 10, ShadingRateSource = 11, VideoDecodeRead = 12, VideoDecodeWrite = 13, @@ -918,8 +919,18 @@ public enum BarrierLayout VideoEncodeRead = 16, VideoEncodeWrite = 17, DirectQueueCommon = 18, - ComputeQueueCommon = 19, - VideoQueueCommon = 20 + DirectQueueGenericRead = 19, + DirectQueueUnorderedAccess = 20, + DirectQueueShaderResource = 21, + DirectQueueCopySource = 22, + DirectQueueCopyDest = 23, + ComputeQueueCommon = 24, + ComputeQueueGenericRead = 25, + ComputeQueueUnorderedAccess = 26, + ComputeQueueShaderResource = 27, + ComputeQueueCopySource = 28, + ComputeQueueCopyDest = 29, + DirectQueueGenericReadComputeQueueAccessible = 31, } [Flags] diff --git a/Ghost.Graphics/RHI/IResourceDatabase.cs b/Ghost.Graphics/RHI/IResourceDatabase.cs index 1e098a8..30500c9 100644 --- a/Ghost.Graphics/RHI/IResourceDatabase.cs +++ b/Ghost.Graphics/RHI/IResourceDatabase.cs @@ -110,6 +110,12 @@ public interface IResourceDatabase : IDisposable /// true if a sampler with the given identifier exists; otherwise, false. bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier id); + /// + /// Releases the sampler associated with the specified identifier and frees any resources allocated to it. + /// + /// The identifier of the sampler to release. Must reference a valid, existing sampler. + void ReleaseSampler(Identifier id); + /// /// Adds a mesh to the resource database and returns its handle. /// diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraph.cs b/Ghost.Graphics/RenderGraphModule/RenderGraph.cs index 1ffcef2..75832ec 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraph.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraph.cs @@ -3,8 +3,8 @@ using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; +using System.Diagnostics; using System.IO.Hashing; -using TerraFX.Interop.Windows; namespace Ghost.Graphics.RenderGraphModule; @@ -25,10 +25,10 @@ public sealed class RenderGraph : IDisposable private readonly RenderGraphBuilder _builder; private readonly ResourceAliasingManager _aliasingManager; - private readonly Dictionary _resourceStates; - private readonly List _barriers; + private readonly Dictionary _resourceStates = new(128); + private readonly List _barriers = new(128); - private readonly RenderGraphCompilationCache _compilationCache; + private readonly RenderGraphCompilationCache _compilationCache = new(); private readonly RenderGraphContext _context; private bool _compiled; @@ -54,7 +54,7 @@ public sealed class RenderGraph : IDisposable _builder = new RenderGraphBuilder(); _aliasingManager = new ResourceAliasingManager(_objectPool); - _resourceStates = new Dictionary(64); + _resourceStates = new Dictionary(64); _barriers = new List(128); _compilationCache = new RenderGraphCompilationCache(); @@ -70,6 +70,7 @@ public sealed class RenderGraph : IDisposable Blackboard = new RenderGraphBlackboard(); } + /// /// Resets the render graph for a new frame. /// Reuses existing allocations to minimize GC. @@ -80,10 +81,10 @@ public sealed class RenderGraph : IDisposable Blackboard.Clear(); // Reset resources but keep allocations - _resources.BeginFrame(); + _resources.Reset(); // Reset aliasing manager - _aliasingManager.BeginFrame(); + _aliasingManager.Reset(); // Clear resource states and barriers _resourceStates.Clear(); @@ -116,7 +117,9 @@ public sealed class RenderGraph : IDisposable /// /// The external texture handle. /// The identifier of the imported render graph texture. Invalid if import fails. - public Identifier ImportTexture(Handle texture, string name) + public Identifier ImportTexture(Handle texture, string name, + Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0, + bool clearAtFirstUse = true, bool discardAtLastUse = true) { var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(texture.AsResource()); if (r.IsFailure) @@ -125,7 +128,7 @@ public sealed class RenderGraph : IDisposable } var desc = r.Value; - return _resources.ImportTexture(in desc._desc.textureDescription, texture, name); + return _resources.ImportTexture(in desc._desc.textureDescription, texture, name, clearColor, clearDepth, clearStencil, clearAtFirstUse, discardAtLastUse); } /// @@ -329,17 +332,6 @@ public sealed class RenderGraph : IDisposable *(int*)(pData + offset) = pass.randomAccess[k].Value; offset += sizeof(int); } - - // Hash buffer hints (important for correct barrier generation) - *(int*)(pData + offset) = pass.bufferHints.Count; - offset += sizeof(int); - foreach (var kvp in pass.bufferHints) - { - *(int*)(pData + offset) = kvp.Key; // Buffer resource ID - offset += sizeof(int); - *(int*)(pData + offset) = (int)kvp.Value; // BufferHint flags - offset += sizeof(int); - } } *(int*)(pData + offset) = pass.GetRenderFuncHashCode(); @@ -439,7 +431,7 @@ public sealed class RenderGraph : IDisposable _aliasingManager.AssignPhysicalResources(_resources, _passes.Count); AllocateResource(); - GenerateBarriers(); + GenerateAliasingBarriers(); BuildNativeRenderPasses(); StoreInCache(graphHash); @@ -570,6 +562,8 @@ public sealed class RenderGraph : IDisposable res.backingResource = cached.backingResources[i]; } } + + BuildNativeRenderPasses(); } /// @@ -667,9 +661,9 @@ public sealed class RenderGraph : IDisposable } /// - /// Generates resource barriers for state transitions and aliasing. + /// Generates aliasing barriers to synchronize resources sharing memory. /// - private void GenerateBarriers() + private void GenerateAliasingBarriers() { _barriers.Clear(); _resourceStates.Clear(); @@ -681,9 +675,6 @@ public sealed class RenderGraph : IDisposable // Insert aliasing barriers for resources that reuse physical memory InsertAliasingBarriers(pass, passIdx); - - // Insert transition barriers for state changes - InsertTransitionBarriers(pass, passIdx); } } @@ -741,27 +732,16 @@ public sealed class RenderGraph : IDisposable } } - // If we found a previous resource, insert aliasing barrier + // If we found a previous resource, insert aliasing barrier (sync update) if (mostRecentLastUse >= 0) { - BarrierDesc desc; - if (resource.type == RenderGraphResourceType.Texture) - { - desc = BarrierDesc.Texture(resource.backingResource, - BarrierSync.All, BarrierSync.None, - BarrierAccess.NoAccess, BarrierAccess.NoAccess, - BarrierLayout.Undefined, BarrierLayout.Common, - discard: true); - } - else - { - desc = BarrierDesc.Buffer(resource.backingResource, - BarrierSync.All, BarrierSync.None, - BarrierAccess.NoAccess, BarrierAccess.NoAccess); - } - - var barrier = ResourceBarrier.Create(passIdx, desc, id); + // Aliasing Requirement: Transition to Undefined, Sync with Predecessor + var targetState = new ResourceBarrierData(BarrierLayout.Undefined, BarrierAccess.NoAccess, BarrierSync.None); + var barrier = ResourceBarrier.CreateAliasing(passIdx, id, resourceBefore, targetState); _barriers.Add(barrier); + + // Update local tracker so subsequent transitions know it's Undefined + _resourceStates[id.Value] = targetState; } } } @@ -770,227 +750,24 @@ public sealed class RenderGraph : IDisposable } } - /// - /// Inserts transition barriers when a resource changes state. - /// - private void InsertTransitionBarriers(RenderGraphPassBase pass, int passIdx) + private ResourceBarrierData GetBufferReadBarrierData(Identifier handle, RenderGraphPassBase pass, RenderGraphResourceType resourceType) { - // Process reads (transition to appropriate state based on resource type and hints) - for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) - { - var readList = pass.resourceReads[i]; - for (var j = 0; j < readList.Count; j++) - { - var handle = readList[j]; - var state = GetBufferReadState(handle, pass, (RenderGraphResourceType)i); - InsertTransitionIfNeeded(handle, state, passIdx); - } - } - - switch (pass.type) - { - case RenderPassType.Raster: - for (var i = 0; i < pass.maxColorIndex; i++) - { - var access = pass.colorAccess[i]; - InsertTransitionIfNeeded(access.id.AsResource(), ResourceState.RenderTarget, passIdx); - } - - if (pass.depthAccess.id.IsValid) - { - var depthAccess = pass.depthAccess; - InsertTransitionIfNeeded(depthAccess.id.AsResource(), ResourceState.DepthWrite, passIdx); - } - - for (var i = 0; i < pass.randomAccess.Count; i++) - { - InsertTransitionIfNeeded(pass.randomAccess[i], ResourceState.UnorderedAccess, passIdx); - } - - break; - case RenderPassType.Compute: - for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) - { - var writeList = pass.resourceWrites[i]; - for (var j = 0; j < writeList.Count; j++) - { - var id = writeList[j]; - InsertTransitionIfNeeded(id, ResourceState.UnorderedAccess, passIdx); - } - } - - break; - case RenderPassType.Unsafe: - for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) - { - var writeList = pass.resourceWrites[i]; - for (var j = 0; j < writeList.Count; j++) - { - var id = writeList[j]; - InsertTransitionIfNeeded(id, ResourceState.RenderTarget, passIdx); - } - } - - for (var i = 0; i < pass.randomAccess.Count; i++) - { - InsertTransitionIfNeeded(pass.randomAccess[i], ResourceState.UnorderedAccess, passIdx); - } - break; - } - - } - - /// - /// Inserts a transition barrier if the resource state changes. - /// - private void InsertTransitionIfNeeded(Identifier resource, ResourceState newState, int passIdx) - { - if (!_resourceStates.TryGetValue(resource.Value, out var currentState)) - { - // First time seeing this resource, assume undefined - // currentState = ResourceState.Common; - var r = _graphicsEngine.ResourceDatabase.GetResourceState(_resources.GetResource(resource).backingResource); - currentState = r.IsSuccess ? r.Value : ResourceState.Common; - } - - if (currentState != newState) - { - var res = _resources.GetResource(resource); - GetBarrierInfo(currentState, out var syncBefore, out var accessBefore, out var layoutBefore); - GetBarrierInfo(newState, out var syncAfter, out var accessAfter, out var layoutAfter); - - BarrierDesc desc; - if (res.type == RenderGraphResourceType.Texture) - { - desc = BarrierDesc.Texture(res.backingResource, - syncBefore, syncAfter, - accessBefore, accessAfter, - layoutBefore, layoutAfter); - } - else - { - desc = BarrierDesc.Buffer(res.backingResource, - syncBefore, syncAfter, - accessBefore, accessAfter); - } - - var barrier = ResourceBarrier.Create(passIdx, desc, resource); - _barriers.Add(barrier); - _resourceStates[resource.Value] = newState; - } - } - - private static void GetBarrierInfo(ResourceState state, out BarrierSync sync, out BarrierAccess access, out BarrierLayout layout) - { - sync = BarrierSync.None; - access = BarrierAccess.Common; - layout = BarrierLayout.Common; - - if (state == ResourceState.Common) - { - return; - } - - if (state.HasFlag(ResourceState.RenderTarget)) - { - sync |= BarrierSync.RenderTarget; - access |= BarrierAccess.RenderTarget; - layout = BarrierLayout.RenderTarget; - } - if (state.HasFlag(ResourceState.DepthWrite)) - { - sync |= BarrierSync.DepthStencil; - access |= BarrierAccess.DepthStencilWrite; - layout = BarrierLayout.DepthStencilWrite; - } - if (state.HasFlag(ResourceState.DepthRead)) - { - sync |= BarrierSync.DepthStencil; - access |= BarrierAccess.DepthStencilRead; - layout = BarrierLayout.DepthStencilRead; - } - if (state.HasFlag(ResourceState.UnorderedAccess)) - { - sync |= BarrierSync.AllShading; - access |= BarrierAccess.UnorderedAccess; - layout = BarrierLayout.UnorderedAccess; - } - if (state.HasFlag(ResourceState.PixelShaderResource)) - { - sync |= BarrierSync.PixelShading; - access |= BarrierAccess.ShaderResource; - layout = BarrierLayout.ShaderResource; - } - if (state.HasFlag(ResourceState.NonPixelShaderResource)) - { - sync |= BarrierSync.NonPixelShading; - access |= BarrierAccess.ShaderResource; - layout = BarrierLayout.ShaderResource; - } - if (state.HasFlag(ResourceState.CopyDest)) - { - sync |= BarrierSync.Copy; - access |= BarrierAccess.CopyDest; - layout = BarrierLayout.CopyDest; - } - if (state.HasFlag(ResourceState.CopySource)) - { - sync |= BarrierSync.Copy; - access |= BarrierAccess.CopySource; - layout = BarrierLayout.CopySource; - } - if (state.HasFlag(ResourceState.VertexAndConstantBuffer)) - { - sync |= BarrierSync.VertexShading; - access |= BarrierAccess.VertexBuffer | BarrierAccess.ConstantBuffer; - layout = BarrierLayout.Common; - } - if (state.HasFlag(ResourceState.IndexBuffer)) - { - sync |= BarrierSync.IndexInput; - access |= BarrierAccess.IndexBuffer; - layout = BarrierLayout.Common; - } - if (state.HasFlag(ResourceState.IndirectArgument)) - { - sync |= BarrierSync.ExecuteIndirect; - access |= BarrierAccess.IndirectArgument; - layout = BarrierLayout.GenericRead; - } - if (state.HasFlag(ResourceState.GenericRead)) - { - layout = BarrierLayout.GenericRead; - } - if (state.HasFlag(ResourceState.Present)) - { - sync = BarrierSync.All; - access = BarrierAccess.Common; - layout = BarrierLayout.Present; - } - } - - /// - /// Determines the appropriate resource state for a buffer read operation based on usage hints. - /// - private static ResourceState GetBufferReadState(Identifier handle, RenderGraphPassBase pass, RenderGraphResourceType resourceType) - { - // Textures always use ShaderResource state if (resourceType == RenderGraphResourceType.Texture) { - return ResourceState.PixelShaderResource | ResourceState.NonPixelShaderResource; + return new ResourceBarrierData(BarrierLayout.ShaderResource, BarrierAccess.ShaderResource, BarrierSync.PixelShading | BarrierSync.NonPixelShading); } - // Check for buffer-specific usage hints - if (pass.bufferHints.TryGetValue(handle.Value, out var hint)) + var sync = BarrierSync.PixelShading | BarrierSync.NonPixelShading; + var access = BarrierAccess.ShaderResource; + + var resource = _resources.GetResource(handle); + if (resource.bufferDesc.Usage.HasFlag(BufferUsage.IndirectArgument)) { - if (hint.HasFlag(BufferHint.IndirectArgument)) - { - return ResourceState.IndirectArgument; - } + sync = BarrierSync.ExecuteIndirect; + access = BarrierAccess.IndirectArgument; } - // Default: ByteAddressBuffer read (SRV) - matches bindless architecture - return ResourceState.PixelShaderResource | ResourceState.NonPixelShaderResource; + return new ResourceBarrierData(BarrierLayout.Undefined, access, sync); } /// @@ -1442,35 +1219,193 @@ public sealed class RenderGraph : IDisposable /// private unsafe void ExecuteBarriersForPass(ICommandBuffer cmd, int passIndex, ref int barrierIndex) { - int start = barrierIndex; - int count = 0; + const int MaxBatch = 64; + var barriers = stackalloc BarrierDesc[MaxBatch]; + var barrierCount = 0; - while (barrierIndex < _barriers.Count && _barriers[barrierIndex].PassIndex == passIndex) + void Flush() { - count++; - barrierIndex++; - } - - if (count > 0) - { - const int BatchSize = 64; - var descs = stackalloc BarrierDesc[BatchSize]; - int processed = 0; - while (processed < count) + if (barrierCount > 0) { - int batch = Math.Min(count - processed, BatchSize); - for (int i = 0; i < batch; i++) - { - descs[i] = _barriers[start + processed + i].Desc; - } - cmd.ResourceBarrier(new ReadOnlySpan(descs, batch)); - processed += batch; + cmd.ResourceBarrier(new ReadOnlySpan(barriers, barrierCount)); + barrierCount = 0; } } + + // 1. Process Aliasing Barriers (Explicitly scheduled) + while (barrierIndex < _barriers.Count && _barriers[barrierIndex].PassIndex == passIndex) + { + var barrierReq = _barriers[barrierIndex++]; + var resourceHandle = _resources.GetResource(barrierReq.Resource).backingResource; + + BarrierLayout layoutBefore; + BarrierAccess accessBefore; + BarrierSync syncBefore; + + if (barrierReq.AliasingPredecessor.IsValid) + { + var predHandle = _resources.GetResource(barrierReq.AliasingPredecessor).backingResource; + var predState = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow(); + + layoutBefore = BarrierLayout.Undefined; + accessBefore = BarrierAccess.NoAccess; + syncBefore = predState.Sync; + } + else + { + var currentState = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow(); + layoutBefore = currentState.Layout; + accessBefore = currentState.Access; + syncBefore = currentState.Sync; + } + + var target = barrierReq.TargetState; + var resType = _resources.GetResource(barrierReq.Resource).type; + + BarrierDesc desc; + if (resType == RenderGraphResourceType.Texture) + { + desc = BarrierDesc.Texture(resourceHandle, + syncBefore, target.Sync, + accessBefore, target.Access, + layoutBefore, target.Layout, + discard: barrierReq.Flags.HasFlag(BarrierFlags.Discard)); + } + else + { + desc = BarrierDesc.Buffer(resourceHandle, + syncBefore, target.Sync, + accessBefore, target.Access); + } + + if (barrierCount >= MaxBatch) + { + Flush(); + } + + barriers[barrierCount++] = desc; + + //_graphicsEngine.ResourceDatabase.SetResourceBarrierData(resourceHandle, target); + } + + // 2. Process Implicit Transitions (Iterate pass resources) + var pass = _compiledPasses[passIndex]; + + // Helper to check and issue transition + void IssueTransition(Identifier id, ResourceBarrierData target) + { + var resource = _resources.GetResource(id); + var handle = resource.backingResource; + var current = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(handle).GetValueOrThrow(); + + if (current.Layout != target.Layout || current.Access != target.Access || current.Sync != target.Sync) + { + BarrierDesc desc; + if (resource.type == RenderGraphResourceType.Texture) + { + desc = BarrierDesc.Texture(handle, + current.Sync, target.Sync, + current.Access, target.Access, + current.Layout, target.Layout); + } + else + { + desc = BarrierDesc.Buffer(handle, + current.Sync, target.Sync, + current.Access, target.Access); + } + + if (barrierCount >= MaxBatch) Flush(); + barriers[barrierCount++] = desc; + + //_graphicsEngine.ResourceDatabase.SetResourceBarrierData(handle, target); + } + } + + // Reads + for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) + { + var readList = pass.resourceReads[i]; + for (var j = 0; j < readList.Count; j++) + { + var handle = readList[j]; + var targetState = GetBufferReadBarrierData(handle, pass, (RenderGraphResourceType)i); + IssueTransition(handle, targetState); + } + } + + switch (pass.type) + { + case RenderPassType.Raster: + for (var i = 0; i <= pass.maxColorIndex; i++) + { + if (pass.colorAccess[i].id.IsValid) + { + var usage = pass.colorAccess[i].usage; + var targetState = new ResourceBarrierData(usage.Layout, usage.Access, usage.Sync); + IssueTransition(pass.colorAccess[i].id.AsResource(), targetState); + } + } + + if (pass.depthAccess.id.IsValid) + { + var usage = pass.depthAccess.usage; + var targetState = new ResourceBarrierData(usage.Layout, usage.Access, usage.Sync); + IssueTransition(pass.depthAccess.id.AsResource(), targetState); + } + + var uavState = new ResourceBarrierData(BarrierLayout.UnorderedAccess, BarrierAccess.UnorderedAccess, BarrierSync.AllShading); + for (var i = 0; i < pass.randomAccess.Count; i++) + { + IssueTransition(pass.randomAccess[i], uavState); + } + break; + + case RenderPassType.Compute: + var computeUavState = new ResourceBarrierData(BarrierLayout.UnorderedAccess, BarrierAccess.UnorderedAccess, BarrierSync.ComputeShading); + for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) + { + var writeList = pass.resourceWrites[i]; + for (var j = 0; j < writeList.Count; j++) + { + IssueTransition(writeList[j], computeUavState); + } + } + break; + + case RenderPassType.Unsafe: + var rtState = new ResourceBarrierData(BarrierLayout.RenderTarget, BarrierAccess.RenderTarget, BarrierSync.RenderTarget); + for (var i = 0; i < (int)RenderGraphResourceType.Count; i++) + { + var writeList = pass.resourceWrites[i]; + for (var j = 0; j < writeList.Count; j++) + { + IssueTransition(writeList[j], rtState); + } + } + + var unsafeUavState = new ResourceBarrierData(BarrierLayout.UnorderedAccess, BarrierAccess.UnorderedAccess, BarrierSync.AllShading); + for (var i = 0; i < pass.randomAccess.Count; i++) + { + IssueTransition(pass.randomAccess[i], unsafeUavState); + } + break; + } + + Flush(); } public void Dispose() { + foreach (var resource in _resources.Resources) + { + _graphicsEngine.ResourceDatabase.ReleaseResource(resource.backingResource); + } + _graphicsEngine.ResourceDatabase.ReleaseResource(_resourceHeap); + + // We need to reset the whole graph to return resources to the pool + // FIX: Ideally we should call dispose here for each subsystem + Reset(); } } diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphAliasing.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphAliasing.cs index 4ff0fae..03bae7e 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphAliasing.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphAliasing.cs @@ -322,7 +322,7 @@ internal sealed class ResourceAliasingManager _logicalToPlaced = new Dictionary(64); } - public void BeginFrame() + public void Reset() { for (var i = 0; i < _placedResources.Count; i++) { diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphBarriers.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphBarriers.cs index 608a2fb..4e938f8 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphBarriers.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphBarriers.cs @@ -4,41 +4,70 @@ using System.Runtime.InteropServices; namespace Ghost.Graphics.RenderGraphModule; +[Flags] +internal enum BarrierFlags +{ + None = 0, + FirstUsage = 1 << 0, + Discard = 1 << 1 +} + /// -/// Represents a resource barrier that needs to be inserted. +/// Represents a resource barrier requirement that needs to be resolved at runtime. /// internal struct ResourceBarrier { public int PassIndex; - public BarrierDesc Desc; - public Identifier LogicalResource; + public Identifier Resource; + public ResourceBarrierData TargetState; + public Identifier AliasingPredecessor; // Invalid if not aliasing + public BarrierFlags Flags; - public readonly Identifier Resource => LogicalResource; - - public static ResourceBarrier Create(int passIndex, BarrierDesc desc, Identifier logicalResource) + public static ResourceBarrier CreateTransition(int passIndex, Identifier resource, ResourceBarrierData targetState, BarrierFlags flags = BarrierFlags.None) { return new ResourceBarrier { PassIndex = passIndex, - Desc = desc, - LogicalResource = logicalResource + Resource = resource, + TargetState = targetState, + AliasingPredecessor = Identifier.Invalid, + Flags = flags }; } + + public static ResourceBarrier CreateAliasing(int passIndex, Identifier resource, Identifier predecessor, ResourceBarrierData targetState) + { + return new ResourceBarrier + { + PassIndex = passIndex, + Resource = resource, + TargetState = targetState, + AliasingPredecessor = predecessor, + Flags = BarrierFlags.FirstUsage | BarrierFlags.Discard // Aliasing implies starting fresh + }; + } + + public override readonly string ToString() + { + return AliasingPredecessor.IsValid + ? $"[Pass {PassIndex}] Aliasing Barrier: {AliasingPredecessor.Value}->{Resource.Value} Target: {TargetState.Layout}" + : $"[Pass {PassIndex}] Barrier: {Resource.Value} Target: {TargetState.Layout}"; + } } /// -/// Tracks the current state of a resource across passes. +/// Tracks the current state of a resource across passes during compilation. /// internal sealed class ResourceStateTracker { public int resourceIndex; - public ResourceState currentState = ResourceState.Common; + public ResourceBarrierData currentState; public int lastAccessPass = -1; public void Reset() { resourceIndex = -1; - currentState = ResourceState.Common; + currentState = default; lastAccessPass = -1; } } diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphBuilder.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphBuilder.cs index e578322..605e82e 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphBuilder.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphBuilder.cs @@ -53,9 +53,9 @@ public interface IRenderGraphBuilder : IDisposable /// /// The identifier of the buffer to be used in the render graph pass. /// The access mode specifying how the buffer will be read or written during the pass. - /// Optional hint about how the buffer will be used (e.g., IndirectArgument). Default is None (ByteAddressBuffer SRV). + /// Optional hint about how the buffer will be used. /// An identifier for the buffer. - Identifier UseBuffer(Identifier buffer, AccessFlags accessMode, BufferHint hint = BufferHint.None); + Identifier UseBuffer(Identifier buffer, AccessFlags accessMode); } public interface IRasterRenderGraphBuilder : IRenderGraphBuilder @@ -212,16 +212,9 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra return UseResource(texture.AsResource(), flags, RenderGraphResourceType.Texture).AsTexture(); } - public Identifier UseBuffer(Identifier buffer, AccessFlags flags, BufferHint hint = BufferHint.None) + public Identifier UseBuffer(Identifier buffer, AccessFlags flags) { ThrowIfDisposed(); - - // Store buffer hint if not None - if (hint != BufferHint.None) - { - _pass.bufferHints[buffer.AsResource().Value] = hint; - } - return UseResource(buffer.AsResource(), flags, RenderGraphResourceType.Buffer).AsBuffer(); } @@ -255,7 +248,8 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra if (_pass.colorAccess[index].id == id || _pass.colorAccess[index].id.IsInvalid) { _pass.maxColorIndex = Math.Max(_pass.maxColorIndex, index); - _pass.colorAccess[index] = new TextureAccess(id, flags); + var usage = new ResourceBarrierData(BarrierLayout.RenderTarget, BarrierAccess.RenderTarget, BarrierSync.RenderTarget); + _pass.colorAccess[index] = new TextureAccess(id, flags, usage); } else { @@ -263,14 +257,18 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra } } - public void SetDepthAttachment(Identifier texture, AccessFlags flags = AccessFlags.Write) + public void SetDepthAttachment(Identifier texture, AccessFlags flags = AccessFlags.ReadWrite) { ThrowIfDisposed(); var id = UseTexture(texture, flags); if (_pass.depthAccess.id == id || _pass.depthAccess.id.IsInvalid) { - _pass.depthAccess = new TextureAccess(id, flags); + var layout = flags.HasFlag(AccessFlags.Write) ? BarrierLayout.DepthStencilWrite : BarrierLayout.DepthStencilRead; + var access = flags.HasFlag(AccessFlags.Write) ? BarrierAccess.DepthStencilWrite : BarrierAccess.DepthStencilRead; + var sync = BarrierSync.DepthStencil; + var usage = new ResourceBarrierData(layout, access, sync); + _pass.depthAccess = new TextureAccess(id, flags, usage); } else { diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphCompilationCache.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphCompilationCache.cs index 337d472..e07646f 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphCompilationCache.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphCompilationCache.cs @@ -27,7 +27,7 @@ internal sealed class CachedCompilation public readonly List barriers = new(128); // Resource state mappings (for barrier generation) - public readonly Dictionary resourceStates = new(128); + public readonly Dictionary resourceStates = new(128); // Real gpu resource public readonly List> backingResources = new(32); diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphPass.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphPass.cs index abd3a46..45809e6 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphPass.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphPass.cs @@ -37,9 +37,6 @@ internal abstract class RenderGraphPassBase public readonly List>[] resourceWrites = new List>[(int)RenderGraphResourceType.Count]; public readonly List>[] resourceCreates = new List>[(int)RenderGraphResourceType.Count]; - // Buffer usage hints (maps buffer resource ID to hint) - public readonly Dictionary bufferHints = new(8); - // Execution state public bool culled; public bool hasSideEffects; @@ -79,8 +76,6 @@ internal abstract class RenderGraphPassBase resourceCreates[i].Clear(); } - bufferHints.Clear(); - culled = false; hasSideEffects = false; } diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphResourcePool.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphResourcePool.cs index 6ebfec1..adcf9c4 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphResourcePool.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphResourcePool.cs @@ -168,7 +168,7 @@ internal sealed class RenderGraphResourceRegistry } } - public void BeginFrame() + public void Reset() { // Return all resources to pool for (var i = 0; i < _resources.Count; i++) @@ -179,13 +179,30 @@ internal sealed class RenderGraphResourceRegistry _resources.Clear(); } - public Identifier ImportTexture(ref readonly TextureDesc desc, Handle texture, string name) + public Identifier ImportTexture(ref readonly TextureDesc desc, Handle texture, string name, + Color128 clearColor, float clearDepth, byte clearStencil, + bool clearAtFirstUse, bool discardAtLastUse) { var resource = _pool.Rent(); resource.name = name; resource.type = RenderGraphResourceType.Texture; resource.index = _resources.Count; - resource.rgTextureDesc = RGTextureDesc.FromTextureDesc(in desc); + resource.rgTextureDesc = new RGTextureDesc + { + sizeMode = RGTextureSizeMode.Absolute, + width = desc.Width, + height = desc.Height, + format = desc.Format, + clearColor = clearColor, + clearDepth = clearDepth, + clearStencil = clearStencil, + clearAtFirstUse = clearAtFirstUse, + discardAtLastUse = discardAtLastUse, + dimension = desc.Dimension, + mipLevels = desc.MipLevels, + slice = desc.Slice, + usage = desc.Usage + }; resource.isImported = true; resource.backingResource = texture.AsResource(); resource.resolvedWidth = desc.Width; diff --git a/Ghost.Graphics/RenderGraphModule/RenderGraphTypes.cs b/Ghost.Graphics/RenderGraphModule/RenderGraphTypes.cs index 1b773fe..1b73d53 100644 --- a/Ghost.Graphics/RenderGraphModule/RenderGraphTypes.cs +++ b/Ghost.Graphics/RenderGraphModule/RenderGraphTypes.cs @@ -225,29 +225,6 @@ public struct RGTextureDesc : IEquatable usage = TextureUsage.DepthStencil | TextureUsage.ShaderResource }; } - - /// - /// Creates an RGTextureDesc from an RHI TextureDesc (for imported textures). - /// - public static RGTextureDesc FromTextureDesc(in TextureDesc desc) - { - return new RGTextureDesc - { - sizeMode = RGTextureSizeMode.Absolute, - width = desc.Width, - height = desc.Height, - format = desc.Format, - clearColor = default, - clearDepth = 1.0f, - clearStencil = 0, - clearAtFirstUse = false, - discardAtLastUse = false, - dimension = desc.Dimension, - mipLevels = desc.MipLevels, - slice = desc.Slice, - usage = desc.Usage - }; - } /// @@ -342,131 +319,37 @@ public static class RGResourceExtensions } } -/// -/// Hints for how a buffer will be used in a pass. -/// Used to determine correct resource state transitions. -/// -[Flags] -public enum BufferHint -{ - /// - /// No special usage - buffer will be used as shader resource (SRV) or UAV based on AccessFlags. - /// - None = 0, - - /// - /// Buffer will be used as indirect argument buffer (ExecuteIndirect). - /// Requires ResourceState.IndirectArgument. - /// - IndirectArgument = 1 << 0, -} - internal readonly struct TextureAccess { public readonly Identifier id; public readonly AccessFlags accessFlags; + public readonly ResourceBarrierData usage; - public TextureAccess(Identifier id, AccessFlags accessFlags) + public TextureAccess(Identifier id, AccessFlags accessFlags, ResourceBarrierData usage) { this.id = id; this.accessFlags = accessFlags; + this.usage = usage; } } /// -/// Tracks buffer access information including usage hints. +/// Tracks buffer access information. /// internal readonly struct BufferAccess { public readonly Identifier id; public readonly AccessFlags accessFlags; - public readonly BufferHint hint; + public readonly ResourceBarrierData usage; - public BufferAccess(Identifier id, AccessFlags accessFlags, BufferHint hint = BufferHint.None) + public BufferAccess(Identifier id, AccessFlags accessFlags, ResourceBarrierData usage) { this.id = id; this.accessFlags = accessFlags; - this.hint = hint; + this.usage = usage; } } -///// -///// Descriptor for creating a texture resource. -///// -//public readonly struct TextureDescriptor : IEquatable -//{ -// public readonly int width; -// public readonly int height; -// public readonly TextureFormat format; -// public readonly string name; - -// public TextureDescriptor(int width, int height, TextureFormat format, string name) -// { -// this.width = width; -// this.height = height; -// this.format = format; -// this.name = name; -// } - -// public readonly bool Equals(TextureDescriptor other) -// { -// return width == other.width && -// height == other.height && -// format == other.format && -// name == other.name; -// } - -// public override readonly bool Equals(object? obj) => obj is TextureDescriptor other && Equals(other); -// public override readonly int GetHashCode() => HashCode.Combine(width, height, format, name); - -// public static bool operator ==(TextureDescriptor left, TextureDescriptor right) -// { -// return left.Equals(right); -// } - -// public static bool operator !=(TextureDescriptor left, TextureDescriptor right) -// { -// return !(left == right); -// } -//} - -//public readonly struct BufferDescriptor : IEquatable -//{ -// public readonly uint sizeInBytes; -// public readonly uint stride; -// public readonly BufferUsage usage; -// public readonly string name; - -// public BufferDescriptor(uint sizeInBytes, uint stride, BufferUsage usage, string name) -// { -// this.sizeInBytes = sizeInBytes; -// this.stride = stride; -// this.usage = usage; -// this.name = name; -// } - -// public readonly bool Equals(BufferDescriptor other) -// { -// return sizeInBytes == other.sizeInBytes && -// stride == other.stride && -// usage == other.usage && -// name == other.name; -// } - -// public override readonly bool Equals(object? obj) => obj is BufferDescriptor other && Equals(other); -// public override readonly int GetHashCode() => HashCode.Combine(sizeInBytes, name); - -// public static bool operator ==(BufferDescriptor left, BufferDescriptor right) -// { -// return left.Equals(right); -// } - -// public static bool operator !=(BufferDescriptor left, BufferDescriptor right) -// { -// return !(left == right); -// } -//} - /// /// Base interface for pass data that can be stored in the blackboard. /// @@ -497,4 +380,4 @@ internal struct DepthStencilInfo public AttachmentStoreOp storeOp; public float clearDepth; public byte clearStencil; -} +} \ No newline at end of file diff --git a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs index 60f4b03..42c8fcc 100644 --- a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs +++ b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs @@ -233,7 +233,7 @@ internal class MeshRenderPass : IRenderPass throw new InvalidOperationException("Failed to get material reference."); } - ref readonly var matRef = ref meshResult.Value; + ref var matRef = ref meshResult.Value; var matProps = new ShaderProperties_MyShader_Standard { color = new float4(1.0f, 1.0f, 1.0f, 1.0f), @@ -271,7 +271,6 @@ internal class MeshRenderPass : IRenderPass }); } - // FIX: We can not upload the blit material properties during a native render pass. using (var builder = graph.AddUnsafeRenderPass("Blit Pass", out var passData)) { passData.source = renderTarget; @@ -281,7 +280,7 @@ internal class MeshRenderPass : IRenderPass builder.UseTexture(passData.source, AccessFlags.Read); builder.UseTexture(passData.destination, AccessFlags.WriteAll); - + builder.SetRenderFunc(static (data, ctx) => { var r = ctx.ResourceDatabase.GetMaterialReference(data.blitMaterial); @@ -290,7 +289,7 @@ internal class MeshRenderPass : IRenderPass return; } - ref readonly var matRef = ref r.Value; + ref var matRef = ref r.Value; var blitProps = new ShaderProperties_Hidden_Blit { mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())), @@ -311,9 +310,12 @@ internal class MeshRenderPass : IRenderPass public void Cleanup(IResourceDatabase resourceDatabase) { + resourceDatabase.ReleaseMaterial(_blitMaterial); + resourceDatabase.ReleaseMaterial(_material); resourceDatabase.ReleaseShader(_shader); resourceDatabase.ReleaseMesh(_mesh); + resourceDatabase.ReleaseSampler(_sampler); if (_textures != null) { diff --git a/Ghost.Graphics/RenderPasses/ShaderCode.hlsl b/Ghost.Graphics/RenderPasses/ShaderCode.hlsl index 2975bb1..598a5a9 100644 --- a/Ghost.Graphics/RenderPasses/ShaderCode.hlsl +++ b/Ghost.Graphics/RenderPasses/ShaderCode.hlsl @@ -8,7 +8,7 @@ struct PixelInput float4 uv : TEXCOORD0; }; -[MESH_SHADER_THREADS(3)] // 3 threads per triangle +[NumThreads(3, 1, 1)] // 3 threads per triangle [OUTPUT_TRIANGLE_TOPOLOGY] void MSMain( uint3 groupThreadID : SV_GroupThreadID, diff --git a/Ghost.Graphics/Shaders/Blit.gsdef b/Ghost.Graphics/Shaders/Blit.gsdef index 9d5b95a..ab7ea18 100644 --- a/Ghost.Graphics/Shaders/Blit.gsdef +++ b/Ghost.Graphics/Shaders/Blit.gsdef @@ -32,7 +32,7 @@ shader "Hidden/Blit" float4 uv : TEXCOORD0; }; - [MESH_SHADER_THREADS(4)] + [NumThreads(4, 1, 1)] [OUTPUT_TRIANGLE_TOPOLOGY] void MSMain( uint gtid : SV_GroupThreadID, @@ -45,7 +45,6 @@ shader "Hidden/Blit" float2 uv = float2(gtid & 1, (gtid >> 1) & 1); verts[gtid].position = float4(uv * 2.0 - 1.0, 0.0, 1.0); - // verts[gtid].position.y *= -1.0; verts[gtid].uv = float4(uv, 0.0, 0.0); if (gtid == 0) diff --git a/Ghost.Graphics/Shaders/Includes/Common.hlsl b/Ghost.Graphics/Shaders/Includes/Common.hlsl index 2d22825..c05f764 100644 --- a/Ghost.Graphics/Shaders/Includes/Common.hlsl +++ b/Ghost.Graphics/Shaders/Includes/Common.hlsl @@ -49,7 +49,6 @@ struct Vertex #define SAMPLE_TEXTURE2D_ARRAY(texId, sampId, uvw) SampleTextureArray(texId, sampId, uvw) -#define MESH_SHADER_THREADS(x) NumThreads(x, 1, 1) #define OUTPUT_TRIANGLE_TOPOLOGY OutputTopology("triangle") #define OUTPUT_LINE_TOPOLOGY OutputTopology("line")