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:
2026-01-22 20:51:58 +09:00
parent 139312d73b
commit 4173ff2432
18 changed files with 395 additions and 488 deletions

View File

@@ -248,7 +248,15 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
break; break;
case BarrierType.Buffer: 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 pBufferBarriers[bufferIndex++] = new D3D12_BUFFER_BARRIER
{ {
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore, SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
@@ -259,11 +267,21 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
Offset = 0, Offset = 0,
Size = ulong.MaxValue Size = ulong.MaxValue
}; };
record.barrierData = new ResourceBarrierData(BarrierLayout.Undefined, desc.AccessAfter, desc.SyncAfter);
} }
break; break;
case BarrierType.Texture: 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 pTextureBarriers[textureIndex++] = new D3D12_TEXTURE_BARRIER
{ {
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore, 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 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; break;
} }

View File

@@ -10,7 +10,7 @@ using static TerraFX.Aliases.D3D12_Alias;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
internal unsafe struct D3D12DescriptorHeap : IDisposable internal unsafe class D3D12DescriptorHeap : IDisposable
{ {
private const int _INVALID_DESCRIPTOR_INDEX = -1; private const int _INVALID_DESCRIPTOR_INDEX = -1;
@@ -51,8 +51,8 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
get; get;
} }
public readonly ID3D12DescriptorHeap* Heap => _heap.Get(); public ID3D12DescriptorHeap* Heap => _heap.Get();
public readonly ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get(); public ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get();
public D3D12DescriptorHeap(string name, D3D12RenderDevice device, D3D12_DESCRIPTOR_HEAP_TYPE type, int numDescriptors) 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) if (index < 0 || index >= NumDescriptors)
{ {
throw new ArgumentOutOfRangeException(nameof(index), "Descriptor index is out of range."); 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) if (index < 0 || index >= NumDescriptors)
{ {
@@ -186,10 +187,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
throw new InvalidOperationException("Descriptor heap is not shader visible."); 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) if (index < 0 || index >= NumDescriptors)
{ {
@@ -201,10 +203,11 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable
throw new InvalidOperationException("Descriptor heap is not shader visible."); 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); _device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
} }

View File

@@ -323,6 +323,15 @@ internal class D3D12ResourceDatabase : IResourceDatabase
return _samplers.TryGetValue(desc, out id); 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) public Handle<Mesh> AddMesh(ref readonly Mesh mesh)
{ {
ObjectDisposedException.ThrowIf(_disposed, this); ObjectDisposedException.ThrowIf(_disposed, this);

View File

@@ -149,7 +149,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
pBackBuffer->SetName($"SwapChain_BackBuffer_{i}"); pBackBuffer->SetName($"SwapChain_BackBuffer_{i}");
var rtv = _descriptorAllocator.AllocateRTV(); 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 var view = ResourceViewGroup.Invalid with
{ {

View File

@@ -837,36 +837,36 @@ public enum BarrierType
} }
[Flags] [Flags]
public enum BarrierSync : uint public enum BarrierSync
{ {
None = 0x0, None = 0x0,
All = 0x0, All = 0x1,
Draw = 0x1, Draw = 0x2,
IndexInput = 0x2, IndexInput = 0x4,
VertexShading = 0x4, VertexShading = 0x8,
PixelShading = 0x8, PixelShading = 0x10,
DepthStencil = 0x10, DepthStencil = 0x20,
RenderTarget = 0x20, RenderTarget = 0x40,
ComputeShading = 0x40, ComputeShading = 0x80,
Raytracing = 0x80, Raytracing = 0x100,
Copy = 0x100, Copy = 0x200,
Resolve = 0x200, Resolve = 0x400,
ExecuteIndirect = 0x400, ExecuteIndirect = 0x800,
Predication = 0x800, Predication = 0x800,
AllShading = VertexShading | PixelShading | ComputeShading | Raytracing, AllShading = 0x1000,
NonPixelShading = VertexShading | ComputeShading | Raytracing, NonPixelShading = 0x2000,
EmitRaytracingAccelerationStructurePostbuildInfo = 0x1000, EmitRaytracingAccelerationStructurePostbuildInfo = 0x4000,
ClearUnorderedAccessView = 0x2000, ClearUnorderedAccessView = 0x8000,
VideoDecode = 0x40000, VideoDecode = 0x100000,
VideoProcess = 0x80000, VideoProcess = 0x200000,
VideoEncode = 0x100000, VideoEncode = 0x400000,
BuildRaytracingAccelerationStructure = 0x200000, BuildRaytracingAccelerationStructure = 0x800000,
CopyRaytracingAccelerationStructure = 0x400000, CopyRaytracingAccelerationStructure = 0x1000000,
Split = 0x800000 Split = unchecked((int)0x80000000)
} }
[Flags] [Flags]
public enum BarrierAccess : uint public enum BarrierAccess
{ {
Common = 0, Common = 0,
VertexBuffer = 0x1, VertexBuffer = 0x1,
@@ -879,6 +879,7 @@ public enum BarrierAccess : uint
ShaderResource = 0x80, ShaderResource = 0x80,
StreamOutput = 0x100, StreamOutput = 0x100,
IndirectArgument = 0x200, IndirectArgument = 0x200,
Predication = 0x200,
CopyDest = 0x400, CopyDest = 0x400,
CopySource = 0x800, CopySource = 0x800,
ResolveDest = 0x1000, ResolveDest = 0x1000,
@@ -892,7 +893,7 @@ public enum BarrierAccess : uint
VideoProcessWrite = 0x100000, VideoProcessWrite = 0x100000,
VideoEncodeRead = 0x200000, VideoEncodeRead = 0x200000,
VideoEncodeWrite = 0x400000, VideoEncodeWrite = 0x400000,
NoAccess = 0x80000000 NoAccess = unchecked((int)0x80000000)
} }
public enum BarrierLayout public enum BarrierLayout
@@ -906,10 +907,10 @@ public enum BarrierLayout
DepthStencilWrite = 4, DepthStencilWrite = 4,
DepthStencilRead = 5, DepthStencilRead = 5,
ShaderResource = 6, ShaderResource = 6,
CopyDest = 7, CopySource = 7,
CopySource = 8, CopyDest = 8,
ResolveDest = 9, ResolveSource = 9,
ResolveSource = 10, ResolveDest = 10,
ShadingRateSource = 11, ShadingRateSource = 11,
VideoDecodeRead = 12, VideoDecodeRead = 12,
VideoDecodeWrite = 13, VideoDecodeWrite = 13,
@@ -918,8 +919,18 @@ public enum BarrierLayout
VideoEncodeRead = 16, VideoEncodeRead = 16,
VideoEncodeWrite = 17, VideoEncodeWrite = 17,
DirectQueueCommon = 18, DirectQueueCommon = 18,
ComputeQueueCommon = 19, DirectQueueGenericRead = 19,
VideoQueueCommon = 20 DirectQueueUnorderedAccess = 20,
DirectQueueShaderResource = 21,
DirectQueueCopySource = 22,
DirectQueueCopyDest = 23,
ComputeQueueCommon = 24,
ComputeQueueGenericRead = 25,
ComputeQueueUnorderedAccess = 26,
ComputeQueueShaderResource = 27,
ComputeQueueCopySource = 28,
ComputeQueueCopyDest = 29,
DirectQueueGenericReadComputeQueueAccessible = 31,
} }
[Flags] [Flags]

View File

@@ -110,6 +110,12 @@ public interface IResourceDatabase : IDisposable
/// <returns>true if a sampler with the given identifier exists; otherwise, false.</returns> /// <returns>true if a sampler with the given identifier exists; otherwise, false.</returns>
bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id); 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> /// <summary>
/// Adds a mesh to the resource database and returns its handle. /// Adds a mesh to the resource database and returns its handle.
/// </summary> /// </summary>

View File

@@ -3,8 +3,8 @@ using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using System.Diagnostics;
using System.IO.Hashing; using System.IO.Hashing;
using TerraFX.Interop.Windows;
namespace Ghost.Graphics.RenderGraphModule; namespace Ghost.Graphics.RenderGraphModule;
@@ -25,10 +25,10 @@ public sealed class RenderGraph : IDisposable
private readonly RenderGraphBuilder _builder; private readonly RenderGraphBuilder _builder;
private readonly ResourceAliasingManager _aliasingManager; private readonly ResourceAliasingManager _aliasingManager;
private readonly Dictionary<int, ResourceState> _resourceStates; private readonly Dictionary<int, ResourceBarrierData> _resourceStates = new(128);
private readonly List<ResourceBarrier> _barriers; private readonly List<ResourceBarrier> _barriers = new(128);
private readonly RenderGraphCompilationCache _compilationCache; private readonly RenderGraphCompilationCache _compilationCache = new();
private readonly RenderGraphContext _context; private readonly RenderGraphContext _context;
private bool _compiled; private bool _compiled;
@@ -54,7 +54,7 @@ public sealed class RenderGraph : IDisposable
_builder = new RenderGraphBuilder(); _builder = new RenderGraphBuilder();
_aliasingManager = new ResourceAliasingManager(_objectPool); _aliasingManager = new ResourceAliasingManager(_objectPool);
_resourceStates = new Dictionary<int, ResourceState>(64); _resourceStates = new Dictionary<int, ResourceBarrierData>(64);
_barriers = new List<ResourceBarrier>(128); _barriers = new List<ResourceBarrier>(128);
_compilationCache = new RenderGraphCompilationCache(); _compilationCache = new RenderGraphCompilationCache();
@@ -70,6 +70,7 @@ public sealed class RenderGraph : IDisposable
Blackboard = new RenderGraphBlackboard(); Blackboard = new RenderGraphBlackboard();
} }
/// <summary> /// <summary>
/// Resets the render graph for a new frame. /// Resets the render graph for a new frame.
/// Reuses existing allocations to minimize GC. /// Reuses existing allocations to minimize GC.
@@ -80,10 +81,10 @@ public sealed class RenderGraph : IDisposable
Blackboard.Clear(); Blackboard.Clear();
// Reset resources but keep allocations // Reset resources but keep allocations
_resources.BeginFrame(); _resources.Reset();
// Reset aliasing manager // Reset aliasing manager
_aliasingManager.BeginFrame(); _aliasingManager.Reset();
// Clear resource states and barriers // Clear resource states and barriers
_resourceStates.Clear(); _resourceStates.Clear();
@@ -116,7 +117,9 @@ public sealed class RenderGraph : IDisposable
/// </summary> /// </summary>
/// <param name="texture">The external texture handle.</param> /// <param name="texture">The external texture handle.</param>
/// <returns>The identifier of the imported render graph texture. Invalid if import fails.</returns> /// <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()); var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(texture.AsResource());
if (r.IsFailure) if (r.IsFailure)
@@ -125,7 +128,7 @@ public sealed class RenderGraph : IDisposable
} }
var desc = r.Value; 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> /// <summary>
@@ -329,17 +332,6 @@ public sealed class RenderGraph : IDisposable
*(int*)(pData + offset) = pass.randomAccess[k].Value; *(int*)(pData + offset) = pass.randomAccess[k].Value;
offset += sizeof(int); 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(); *(int*)(pData + offset) = pass.GetRenderFuncHashCode();
@@ -439,7 +431,7 @@ public sealed class RenderGraph : IDisposable
_aliasingManager.AssignPhysicalResources(_resources, _passes.Count); _aliasingManager.AssignPhysicalResources(_resources, _passes.Count);
AllocateResource(); AllocateResource();
GenerateBarriers(); GenerateAliasingBarriers();
BuildNativeRenderPasses(); BuildNativeRenderPasses();
StoreInCache(graphHash); StoreInCache(graphHash);
@@ -570,6 +562,8 @@ public sealed class RenderGraph : IDisposable
res.backingResource = cached.backingResources[i]; res.backingResource = cached.backingResources[i];
} }
} }
BuildNativeRenderPasses();
} }
/// <summary> /// <summary>
@@ -667,9 +661,9 @@ public sealed class RenderGraph : IDisposable
} }
/// <summary> /// <summary>
/// Generates resource barriers for state transitions and aliasing. /// Generates aliasing barriers to synchronize resources sharing memory.
/// </summary> /// </summary>
private void GenerateBarriers() private void GenerateAliasingBarriers()
{ {
_barriers.Clear(); _barriers.Clear();
_resourceStates.Clear(); _resourceStates.Clear();
@@ -681,9 +675,6 @@ public sealed class RenderGraph : IDisposable
// Insert aliasing barriers for resources that reuse physical memory // Insert aliasing barriers for resources that reuse physical memory
InsertAliasingBarriers(pass, passIdx); 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) if (mostRecentLastUse >= 0)
{ {
BarrierDesc desc; // Aliasing Requirement: Transition to Undefined, Sync with Predecessor
if (resource.type == RenderGraphResourceType.Texture) var targetState = new ResourceBarrierData(BarrierLayout.Undefined, BarrierAccess.NoAccess, BarrierSync.None);
{ var barrier = ResourceBarrier.CreateAliasing(passIdx, id, resourceBefore, targetState);
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);
_barriers.Add(barrier); _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> private ResourceBarrierData GetBufferReadBarrierData(Identifier<RGResource> handle, RenderGraphPassBase pass, RenderGraphResourceType resourceType)
/// Inserts transition barriers when a resource changes state.
/// </summary>
private void InsertTransitionBarriers(RenderGraphPassBase pass, int passIdx)
{ {
// 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) 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 var sync = BarrierSync.PixelShading | BarrierSync.NonPixelShading;
if (pass.bufferHints.TryGetValue(handle.Value, out var hint)) var access = BarrierAccess.ShaderResource;
var resource = _resources.GetResource(handle);
if (resource.bufferDesc.Usage.HasFlag(BufferUsage.IndirectArgument))
{ {
if (hint.HasFlag(BufferHint.IndirectArgument)) sync = BarrierSync.ExecuteIndirect;
{ access = BarrierAccess.IndirectArgument;
return ResourceState.IndirectArgument;
}
} }
// Default: ByteAddressBuffer read (SRV) - matches bindless architecture return new ResourceBarrierData(BarrierLayout.Undefined, access, sync);
return ResourceState.PixelShaderResource | ResourceState.NonPixelShaderResource;
} }
/// <summary> /// <summary>
@@ -1442,35 +1219,193 @@ public sealed class RenderGraph : IDisposable
/// </summary> /// </summary>
private unsafe void ExecuteBarriersForPass(ICommandBuffer cmd, int passIndex, ref int barrierIndex) private unsafe void ExecuteBarriersForPass(ICommandBuffer cmd, int passIndex, ref int barrierIndex)
{ {
int start = barrierIndex; const int MaxBatch = 64;
int count = 0; var barriers = stackalloc BarrierDesc[MaxBatch];
var barrierCount = 0;
while (barrierIndex < _barriers.Count && _barriers[barrierIndex].PassIndex == passIndex) void Flush()
{ {
count++; if (barrierCount > 0)
barrierIndex++;
}
if (count > 0)
{
const int BatchSize = 64;
var descs = stackalloc BarrierDesc[BatchSize];
int processed = 0;
while (processed < count)
{ {
int batch = Math.Min(count - processed, BatchSize); cmd.ResourceBarrier(new ReadOnlySpan<BarrierDesc>(barriers, barrierCount));
for (int i = 0; i < batch; i++) barrierCount = 0;
{
descs[i] = _barriers[start + processed + i].Desc;
}
cmd.ResourceBarrier(new ReadOnlySpan<BarrierDesc>(descs, batch));
processed += batch;
} }
} }
// 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<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() public void Dispose()
{ {
foreach (var resource in _resources.Resources)
{
_graphicsEngine.ResourceDatabase.ReleaseResource(resource.backingResource);
}
_graphicsEngine.ResourceDatabase.ReleaseResource(_resourceHeap); _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();
} }
} }

View File

@@ -322,7 +322,7 @@ internal sealed class ResourceAliasingManager
_logicalToPlaced = new Dictionary<int, int>(64); _logicalToPlaced = new Dictionary<int, int>(64);
} }
public void BeginFrame() public void Reset()
{ {
for (var i = 0; i < _placedResources.Count; i++) for (var i = 0; i < _placedResources.Count; i++)
{ {

View File

@@ -4,41 +4,70 @@ using System.Runtime.InteropServices;
namespace Ghost.Graphics.RenderGraphModule; namespace Ghost.Graphics.RenderGraphModule;
[Flags]
internal enum BarrierFlags
{
None = 0,
FirstUsage = 1 << 0,
Discard = 1 << 1
}
/// <summary> /// <summary>
/// Represents a resource barrier that needs to be inserted. /// Represents a resource barrier requirement that needs to be resolved at runtime.
/// </summary> /// </summary>
internal struct ResourceBarrier internal struct ResourceBarrier
{ {
public int PassIndex; public int PassIndex;
public BarrierDesc Desc; public Identifier<RGResource> Resource;
public Identifier<RGResource> LogicalResource; public ResourceBarrierData TargetState;
public Identifier<RGResource> AliasingPredecessor; // Invalid if not aliasing
public BarrierFlags Flags;
public readonly Identifier<RGResource> Resource => LogicalResource; public static ResourceBarrier CreateTransition(int passIndex, Identifier<RGResource> resource, ResourceBarrierData targetState, BarrierFlags flags = BarrierFlags.None)
public static ResourceBarrier Create(int passIndex, BarrierDesc desc, Identifier<RGResource> logicalResource)
{ {
return new ResourceBarrier return new ResourceBarrier
{ {
PassIndex = passIndex, PassIndex = passIndex,
Desc = desc, Resource = resource,
LogicalResource = logicalResource 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> /// <summary>
/// Tracks the current state of a resource across passes. /// Tracks the current state of a resource across passes during compilation.
/// </summary> /// </summary>
internal sealed class ResourceStateTracker internal sealed class ResourceStateTracker
{ {
public int resourceIndex; public int resourceIndex;
public ResourceState currentState = ResourceState.Common; public ResourceBarrierData currentState;
public int lastAccessPass = -1; public int lastAccessPass = -1;
public void Reset() public void Reset()
{ {
resourceIndex = -1; resourceIndex = -1;
currentState = ResourceState.Common; currentState = default;
lastAccessPass = -1; lastAccessPass = -1;
} }
} }

View File

@@ -53,9 +53,9 @@ public interface IRenderGraphBuilder : IDisposable
/// </summary> /// </summary>
/// <param name="buffer">The identifier of the buffer to be used in the render graph pass.</param> /// <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="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> /// <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 public interface IRasterRenderGraphBuilder : IRenderGraphBuilder
@@ -212,16 +212,9 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
return UseResource(texture.AsResource(), flags, RenderGraphResourceType.Texture).AsTexture(); 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(); 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(); 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) if (_pass.colorAccess[index].id == id || _pass.colorAccess[index].id.IsInvalid)
{ {
_pass.maxColorIndex = Math.Max(_pass.maxColorIndex, index); _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 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(); ThrowIfDisposed();
var id = UseTexture(texture, flags); var id = UseTexture(texture, flags);
if (_pass.depthAccess.id == id || _pass.depthAccess.id.IsInvalid) 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 else
{ {

View File

@@ -27,7 +27,7 @@ internal sealed class CachedCompilation
public readonly List<ResourceBarrier> barriers = new(128); public readonly List<ResourceBarrier> barriers = new(128);
// Resource state mappings (for barrier generation) // 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 // Real gpu resource
public readonly List<Handle<GPUResource>> backingResources = new(32); public readonly List<Handle<GPUResource>> backingResources = new(32);

View File

@@ -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>>[] resourceWrites = new List<Identifier<RGResource>>[(int)RenderGraphResourceType.Count];
public readonly List<Identifier<RGResource>>[] resourceCreates = 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 // Execution state
public bool culled; public bool culled;
public bool hasSideEffects; public bool hasSideEffects;
@@ -79,8 +76,6 @@ internal abstract class RenderGraphPassBase
resourceCreates[i].Clear(); resourceCreates[i].Clear();
} }
bufferHints.Clear();
culled = false; culled = false;
hasSideEffects = false; hasSideEffects = false;
} }

View File

@@ -168,7 +168,7 @@ internal sealed class RenderGraphResourceRegistry
} }
} }
public void BeginFrame() public void Reset()
{ {
// Return all resources to pool // Return all resources to pool
for (var i = 0; i < _resources.Count; i++) for (var i = 0; i < _resources.Count; i++)
@@ -179,13 +179,30 @@ internal sealed class RenderGraphResourceRegistry
_resources.Clear(); _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>(); var resource = _pool.Rent<RenderGraphResource>();
resource.name = name; resource.name = name;
resource.type = RenderGraphResourceType.Texture; resource.type = RenderGraphResourceType.Texture;
resource.index = _resources.Count; 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.isImported = true;
resource.backingResource = texture.AsResource(); resource.backingResource = texture.AsResource();
resource.resolvedWidth = desc.Width; resource.resolvedWidth = desc.Width;

View File

@@ -225,29 +225,6 @@ public struct RGTextureDesc : IEquatable<RGTextureDesc>
usage = TextureUsage.DepthStencil | TextureUsage.ShaderResource usage = TextureUsage.DepthStencil | TextureUsage.ShaderResource
}; };
} }
/// <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> /// <summary>
@@ -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 internal readonly struct TextureAccess
{ {
public readonly Identifier<RGTexture> id; public readonly Identifier<RGTexture> id;
public readonly AccessFlags accessFlags; 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.id = id;
this.accessFlags = accessFlags; this.accessFlags = accessFlags;
this.usage = usage;
} }
} }
/// <summary> /// <summary>
/// Tracks buffer access information including usage hints. /// Tracks buffer access information.
/// </summary> /// </summary>
internal readonly struct BufferAccess internal readonly struct BufferAccess
{ {
public readonly Identifier<RGBuffer> id; public readonly Identifier<RGBuffer> id;
public readonly AccessFlags accessFlags; 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.id = id;
this.accessFlags = accessFlags; 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> /// <summary>
/// Base interface for pass data that can be stored in the blackboard. /// Base interface for pass data that can be stored in the blackboard.
/// </summary> /// </summary>
@@ -497,4 +380,4 @@ internal struct DepthStencilInfo
public AttachmentStoreOp storeOp; public AttachmentStoreOp storeOp;
public float clearDepth; public float clearDepth;
public byte clearStencil; public byte clearStencil;
} }

View File

@@ -233,7 +233,7 @@ internal class MeshRenderPass : IRenderPass
throw new InvalidOperationException("Failed to get material reference."); 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 var matProps = new ShaderProperties_MyShader_Standard
{ {
color = new float4(1.0f, 1.0f, 1.0f, 1.0f), 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)) using (var builder = graph.AddUnsafeRenderPass<BlitPassData>("Blit Pass", out var passData))
{ {
passData.source = renderTarget; passData.source = renderTarget;
@@ -281,7 +280,7 @@ internal class MeshRenderPass : IRenderPass
builder.UseTexture(passData.source, AccessFlags.Read); builder.UseTexture(passData.source, AccessFlags.Read);
builder.UseTexture(passData.destination, AccessFlags.WriteAll); builder.UseTexture(passData.destination, AccessFlags.WriteAll);
builder.SetRenderFunc<BlitPassData>(static (data, ctx) => builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
{ {
var r = ctx.ResourceDatabase.GetMaterialReference(data.blitMaterial); var r = ctx.ResourceDatabase.GetMaterialReference(data.blitMaterial);
@@ -290,7 +289,7 @@ internal class MeshRenderPass : IRenderPass
return; return;
} }
ref readonly var matRef = ref r.Value; ref var matRef = ref r.Value;
var blitProps = new ShaderProperties_Hidden_Blit var blitProps = new ShaderProperties_Hidden_Blit
{ {
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())), mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
@@ -311,9 +310,12 @@ internal class MeshRenderPass : IRenderPass
public void Cleanup(IResourceDatabase resourceDatabase) public void Cleanup(IResourceDatabase resourceDatabase)
{ {
resourceDatabase.ReleaseMaterial(_blitMaterial);
resourceDatabase.ReleaseMaterial(_material); resourceDatabase.ReleaseMaterial(_material);
resourceDatabase.ReleaseShader(_shader); resourceDatabase.ReleaseShader(_shader);
resourceDatabase.ReleaseMesh(_mesh); resourceDatabase.ReleaseMesh(_mesh);
resourceDatabase.ReleaseSampler(_sampler);
if (_textures != null) if (_textures != null)
{ {

View File

@@ -8,7 +8,7 @@ struct PixelInput
float4 uv : TEXCOORD0; float4 uv : TEXCOORD0;
}; };
[MESH_SHADER_THREADS(3)] // 3 threads per triangle [NumThreads(3, 1, 1)] // 3 threads per triangle
[OUTPUT_TRIANGLE_TOPOLOGY] [OUTPUT_TRIANGLE_TOPOLOGY]
void MSMain( void MSMain(
uint3 groupThreadID : SV_GroupThreadID, uint3 groupThreadID : SV_GroupThreadID,

View File

@@ -32,7 +32,7 @@ shader "Hidden/Blit"
float4 uv : TEXCOORD0; float4 uv : TEXCOORD0;
}; };
[MESH_SHADER_THREADS(4)] [NumThreads(4, 1, 1)]
[OUTPUT_TRIANGLE_TOPOLOGY] [OUTPUT_TRIANGLE_TOPOLOGY]
void MSMain( void MSMain(
uint gtid : SV_GroupThreadID, uint gtid : SV_GroupThreadID,
@@ -45,7 +45,6 @@ shader "Hidden/Blit"
float2 uv = float2(gtid & 1, (gtid >> 1) & 1); float2 uv = float2(gtid & 1, (gtid >> 1) & 1);
verts[gtid].position = float4(uv * 2.0 - 1.0, 0.0, 1.0); 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); verts[gtid].uv = float4(uv, 0.0, 0.0);
if (gtid == 0) if (gtid == 0)

View File

@@ -49,7 +49,6 @@ struct Vertex
#define SAMPLE_TEXTURE2D_ARRAY(texId, sampId, uvw) SampleTextureArray(texId, sampId, uvw) #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_TRIANGLE_TOPOLOGY OutputTopology("triangle")
#define OUTPUT_LINE_TOPOLOGY OutputTopology("line") #define OUTPUT_LINE_TOPOLOGY OutputTopology("line")