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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -323,6 +323,15 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
return _samplers.TryGetValue(desc, out id);
|
||||
}
|
||||
|
||||
public void ReleaseSampler(Identifier<Sampler> 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<SamplerDescriptor>(id.Value));
|
||||
}
|
||||
|
||||
public Handle<Mesh> AddMesh(ref readonly Mesh mesh)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -110,6 +110,12 @@ public interface IResourceDatabase : IDisposable
|
||||
/// <returns>true if a sampler with the given identifier exists; otherwise, false.</returns>
|
||||
bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the sampler associated with the specified identifier and frees any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the sampler to release. Must reference a valid, existing sampler.</param>
|
||||
void ReleaseSampler(Identifier<Sampler> id);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a mesh to the resource database and returns its handle.
|
||||
/// </summary>
|
||||
|
||||
@@ -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<int, ResourceState> _resourceStates;
|
||||
private readonly List<ResourceBarrier> _barriers;
|
||||
private readonly Dictionary<int, ResourceBarrierData> _resourceStates = new(128);
|
||||
private readonly List<ResourceBarrier> _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<int, ResourceState>(64);
|
||||
_resourceStates = new Dictionary<int, ResourceBarrierData>(64);
|
||||
_barriers = new List<ResourceBarrier>(128);
|
||||
|
||||
_compilationCache = new RenderGraphCompilationCache();
|
||||
@@ -70,6 +70,7 @@ public sealed class RenderGraph : IDisposable
|
||||
Blackboard = new RenderGraphBlackboard();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="texture">The external texture handle.</param>
|
||||
/// <returns>The identifier of the imported render graph texture. Invalid if import fails.</returns>
|
||||
public Identifier<RGTexture> ImportTexture(Handle<Texture> texture, string name)
|
||||
public Identifier<RGTexture> ImportTexture(Handle<Texture> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -667,9 +661,9 @@ public sealed class RenderGraph : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates resource barriers for state transitions and aliasing.
|
||||
/// Generates aliasing barriers to synchronize resources sharing memory.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts transition barriers when a resource changes state.
|
||||
/// </summary>
|
||||
private void InsertTransitionBarriers(RenderGraphPassBase pass, int passIdx)
|
||||
private ResourceBarrierData GetBufferReadBarrierData(Identifier<RGResource> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a transition barrier if the resource state changes.
|
||||
/// </summary>
|
||||
private void InsertTransitionIfNeeded(Identifier<RGResource> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the appropriate resource state for a buffer read operation based on usage hints.
|
||||
/// </summary>
|
||||
private static ResourceState GetBufferReadState(Identifier<RGResource> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1442,35 +1219,193 @@ public sealed class RenderGraph : IDisposable
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
void Flush()
|
||||
{
|
||||
if (barrierCount > 0)
|
||||
{
|
||||
cmd.ResourceBarrier(new ReadOnlySpan<BarrierDesc>(barriers, barrierCount));
|
||||
barrierCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Process Aliasing Barriers (Explicitly scheduled)
|
||||
while (barrierIndex < _barriers.Count && _barriers[barrierIndex].PassIndex == passIndex)
|
||||
{
|
||||
count++;
|
||||
barrierIndex++;
|
||||
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;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
var target = barrierReq.TargetState;
|
||||
var resType = _resources.GetResource(barrierReq.Resource).type;
|
||||
|
||||
BarrierDesc desc;
|
||||
if (resType == RenderGraphResourceType.Texture)
|
||||
{
|
||||
const int BatchSize = 64;
|
||||
var descs = stackalloc BarrierDesc[BatchSize];
|
||||
int processed = 0;
|
||||
while (processed < count)
|
||||
{
|
||||
int batch = Math.Min(count - processed, BatchSize);
|
||||
for (int i = 0; i < batch; i++)
|
||||
{
|
||||
descs[i] = _barriers[start + processed + i].Desc;
|
||||
desc = BarrierDesc.Texture(resourceHandle,
|
||||
syncBefore, target.Sync,
|
||||
accessBefore, target.Access,
|
||||
layoutBefore, target.Layout,
|
||||
discard: barrierReq.Flags.HasFlag(BarrierFlags.Discard));
|
||||
}
|
||||
cmd.ResourceBarrier(new ReadOnlySpan<BarrierDesc>(descs, batch));
|
||||
processed += batch;
|
||||
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<RGResource> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ internal sealed class ResourceAliasingManager
|
||||
_logicalToPlaced = new Dictionary<int, int>(64);
|
||||
}
|
||||
|
||||
public void BeginFrame()
|
||||
public void Reset()
|
||||
{
|
||||
for (var i = 0; i < _placedResources.Count; i++)
|
||||
{
|
||||
|
||||
@@ -4,41 +4,70 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
[Flags]
|
||||
internal enum BarrierFlags
|
||||
{
|
||||
None = 0,
|
||||
FirstUsage = 1 << 0,
|
||||
Discard = 1 << 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource barrier that needs to be inserted.
|
||||
/// Represents a resource barrier requirement that needs to be resolved at runtime.
|
||||
/// </summary>
|
||||
internal struct ResourceBarrier
|
||||
{
|
||||
public int PassIndex;
|
||||
public BarrierDesc Desc;
|
||||
public Identifier<RGResource> LogicalResource;
|
||||
public Identifier<RGResource> Resource;
|
||||
public ResourceBarrierData TargetState;
|
||||
public Identifier<RGResource> AliasingPredecessor; // Invalid if not aliasing
|
||||
public BarrierFlags Flags;
|
||||
|
||||
public readonly Identifier<RGResource> Resource => LogicalResource;
|
||||
|
||||
public static ResourceBarrier Create(int passIndex, BarrierDesc desc, Identifier<RGResource> logicalResource)
|
||||
public static ResourceBarrier CreateTransition(int passIndex, Identifier<RGResource> resource, ResourceBarrierData targetState, BarrierFlags flags = BarrierFlags.None)
|
||||
{
|
||||
return new ResourceBarrier
|
||||
{
|
||||
PassIndex = passIndex,
|
||||
Desc = desc,
|
||||
LogicalResource = logicalResource
|
||||
Resource = resource,
|
||||
TargetState = targetState,
|
||||
AliasingPredecessor = Identifier<RGResource>.Invalid,
|
||||
Flags = flags
|
||||
};
|
||||
}
|
||||
|
||||
public static ResourceBarrier CreateAliasing(int passIndex, Identifier<RGResource> resource, Identifier<RGResource> 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}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks the current state of a resource across passes.
|
||||
/// Tracks the current state of a resource across passes during compilation.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,9 @@ public interface IRenderGraphBuilder : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="buffer">The identifier of the buffer to be used in the render graph pass.</param>
|
||||
/// <param name="accessMode">The access mode specifying how the buffer will be read or written during the pass.</param>
|
||||
/// <param name="hint">Optional hint about how the buffer will be used (e.g., IndirectArgument). Default is None (ByteAddressBuffer SRV).</param>
|
||||
/// <param name="hint">Optional hint about how the buffer will be used.</param>
|
||||
/// <returns>An identifier for the buffer.</returns>
|
||||
Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> buffer, AccessFlags accessMode, BufferHint hint = BufferHint.None);
|
||||
Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> 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<RGBuffer> UseBuffer(Identifier<RGBuffer> buffer, AccessFlags flags, BufferHint hint = BufferHint.None)
|
||||
public Identifier<RGBuffer> UseBuffer(Identifier<RGBuffer> 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<RGTexture> texture, AccessFlags flags = AccessFlags.Write)
|
||||
public void SetDepthAttachment(Identifier<RGTexture> 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
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ internal sealed class CachedCompilation
|
||||
public readonly List<ResourceBarrier> barriers = new(128);
|
||||
|
||||
// Resource state mappings (for barrier generation)
|
||||
public readonly Dictionary<int, ResourceState> resourceStates = new(128);
|
||||
public readonly Dictionary<int, ResourceBarrierData> resourceStates = new(128);
|
||||
|
||||
// Real gpu resource
|
||||
public readonly List<Handle<GPUResource>> backingResources = new(32);
|
||||
|
||||
@@ -37,9 +37,6 @@ internal abstract class RenderGraphPassBase
|
||||
public readonly List<Identifier<RGResource>>[] resourceWrites = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
|
||||
public readonly List<Identifier<RGResource>>[] resourceCreates = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
|
||||
|
||||
// Buffer usage hints (maps buffer resource ID to hint)
|
||||
public readonly Dictionary<int, BufferHint> 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;
|
||||
}
|
||||
|
||||
@@ -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<RGTexture> ImportTexture(ref readonly TextureDesc desc, Handle<Texture> texture, string name)
|
||||
public Identifier<RGTexture> ImportTexture(ref readonly TextureDesc desc, Handle<Texture> texture, string name,
|
||||
Color128 clearColor, float clearDepth, byte clearStencil,
|
||||
bool clearAtFirstUse, bool discardAtLastUse)
|
||||
{
|
||||
var resource = _pool.Rent<RenderGraphResource>();
|
||||
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;
|
||||
|
||||
@@ -226,29 +226,6 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an RGTextureDesc from an RHI TextureDesc (for imported textures).
|
||||
/// </summary>
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts to RHI TextureDesc using resolved dimensions.
|
||||
@@ -342,131 +319,37 @@ public static class RGResourceExtensions
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hints for how a buffer will be used in a pass.
|
||||
/// Used to determine correct resource state transitions.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum BufferHint
|
||||
{
|
||||
/// <summary>
|
||||
/// No special usage - buffer will be used as shader resource (SRV) or UAV based on AccessFlags.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Buffer will be used as indirect argument buffer (ExecuteIndirect).
|
||||
/// Requires ResourceState.IndirectArgument.
|
||||
/// </summary>
|
||||
IndirectArgument = 1 << 0,
|
||||
}
|
||||
|
||||
internal readonly struct TextureAccess
|
||||
{
|
||||
public readonly Identifier<RGTexture> id;
|
||||
public readonly AccessFlags accessFlags;
|
||||
public readonly ResourceBarrierData usage;
|
||||
|
||||
public TextureAccess(Identifier<RGTexture> id, AccessFlags accessFlags)
|
||||
public TextureAccess(Identifier<RGTexture> id, AccessFlags accessFlags, ResourceBarrierData usage)
|
||||
{
|
||||
this.id = id;
|
||||
this.accessFlags = accessFlags;
|
||||
this.usage = usage;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks buffer access information including usage hints.
|
||||
/// Tracks buffer access information.
|
||||
/// </summary>
|
||||
internal readonly struct BufferAccess
|
||||
{
|
||||
public readonly Identifier<RGBuffer> id;
|
||||
public readonly AccessFlags accessFlags;
|
||||
public readonly BufferHint hint;
|
||||
public readonly ResourceBarrierData usage;
|
||||
|
||||
public BufferAccess(Identifier<RGBuffer> id, AccessFlags accessFlags, BufferHint hint = BufferHint.None)
|
||||
public BufferAccess(Identifier<RGBuffer> id, AccessFlags accessFlags, ResourceBarrierData usage)
|
||||
{
|
||||
this.id = id;
|
||||
this.accessFlags = accessFlags;
|
||||
this.hint = hint;
|
||||
this.usage = usage;
|
||||
}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Descriptor for creating a texture resource.
|
||||
///// </summary>
|
||||
//public readonly struct TextureDescriptor : IEquatable<TextureDescriptor>
|
||||
//{
|
||||
// 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<BufferDescriptor>
|
||||
//{
|
||||
// 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);
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for pass data that can be stored in the blackboard.
|
||||
/// </summary>
|
||||
|
||||
@@ -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<BlitPassData>("Blit Pass", out var passData))
|
||||
{
|
||||
passData.source = renderTarget;
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user