feat(rhi): refactor resource & barrier management for D3D12

Modernizes resource and barrier management for the D3D12 backend. Key changes:
- Simplifies BarrierDesc by removing nullable "before" states; now inferred from resource database.
- Adds IsAliasing flag to BarrierDesc for aliasing transitions.
- Replaces ResourceMemoryType with HeapType in BufferDesc and related APIs.
- Enhances ResourceViewGroup with usage inference methods.
- Adds D3D12 utility helpers for heap/flag conversions and resource description extraction.
- Optimizes command buffer barrier emission, skipping redundant barriers.
- Refactors Material and RenderContext to use new APIs and state tracking.
- Updates ResourceManager pooling to use HeapType and standard Queue.
- Simplifies RenderGraphExecutor barrier logic and aliasing handling.
- Improves RenderSystem frame synchronization and resource retirement.
- Cleans up obsolete code and improves debug output.

BREAKING CHANGE: Updates to resource and barrier APIs require changes to all code interfacing with resource creation, barriers, and memory types.
This commit is contained in:
2026-04-03 17:03:41 +09:00
parent 6321b36ef5
commit ba9e24c46c
20 changed files with 316 additions and 422 deletions

View File

@@ -200,13 +200,20 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
switch (desc.Type) switch (desc.Type)
{ {
case BarrierType.Global: case BarrierType.Global:
if (desc.SyncAfter == _resourceDatabase.globalBarrier.sync && desc.AccessAfter == _resourceDatabase.globalBarrier.access)
{
continue;
}
pGlobalBarriers[globalIndex++] = new D3D12_GLOBAL_BARRIER pGlobalBarriers[globalIndex++] = new D3D12_GLOBAL_BARRIER
{ {
SyncBefore = (D3D12_BARRIER_SYNC)(desc.SyncBefore ?? 0), SyncBefore = (D3D12_BARRIER_SYNC)_resourceDatabase.globalBarrier.sync,
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter, SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
AccessBefore = (D3D12_BARRIER_ACCESS)(desc.AccessBefore ?? 0), AccessBefore = (D3D12_BARRIER_ACCESS)_resourceDatabase.globalBarrier.access,
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter
}; };
_resourceDatabase.globalBarrier = new ResourceBarrierData(BarrierLayout.Undefined, desc.AccessAfter, desc.SyncAfter);
break; break;
case BarrierType.Buffer: case BarrierType.Buffer:
{ {
@@ -218,12 +225,19 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
} }
ref var record = ref r.Value; ref var record = ref r.Value;
var accessBefore = desc.IsAliasing ? BarrierAccess.NoAccess : record.barrierData.access;
if (record.barrierData.sync == desc.SyncAfter && accessBefore == desc.AccessAfter)
{
continue;
}
var resource = record.ResourcePtr; var resource = record.ResourcePtr;
pBufferBarriers[bufferIndex++] = new D3D12_BUFFER_BARRIER pBufferBarriers[bufferIndex++] = new D3D12_BUFFER_BARRIER
{ {
SyncBefore = (D3D12_BARRIER_SYNC)(desc.SyncBefore ?? record.barrierData.sync), SyncBefore = (D3D12_BARRIER_SYNC)record.barrierData.sync,
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter, SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
AccessBefore = (D3D12_BARRIER_ACCESS)(desc.AccessBefore ?? record.barrierData.access), AccessBefore = (D3D12_BARRIER_ACCESS)accessBefore,
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter, AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter,
pResource = resource, pResource = resource,
Offset = 0, Offset = 0,
@@ -243,14 +257,22 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
} }
ref var record = ref r.Value; ref var record = ref r.Value;
var accessBefore = desc.IsAliasing ? BarrierAccess.NoAccess : record.barrierData.access;
var layoutBefore = desc.IsAliasing ? BarrierLayout.Undefined : record.barrierData.layout;
if (record.barrierData.sync == desc.SyncAfter && accessBefore == desc.AccessAfter && layoutBefore == desc.LayoutAfter)
{
continue;
}
var resource = record.ResourcePtr; var resource = record.ResourcePtr;
pTextureBarriers[textureIndex++] = new D3D12_TEXTURE_BARRIER pTextureBarriers[textureIndex++] = new D3D12_TEXTURE_BARRIER
{ {
SyncBefore = (D3D12_BARRIER_SYNC)(desc.SyncBefore ?? record.barrierData.sync), SyncBefore = (D3D12_BARRIER_SYNC)record.barrierData.sync,
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter, SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
AccessBefore = (D3D12_BARRIER_ACCESS)(desc.AccessBefore ?? record.barrierData.access), AccessBefore = (D3D12_BARRIER_ACCESS)accessBefore,
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter, AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter,
LayoutBefore = (D3D12_BARRIER_LAYOUT)(desc.LayoutBefore ?? record.barrierData.layout), LayoutBefore = (D3D12_BARRIER_LAYOUT)layoutBefore,
LayoutAfter = (D3D12_BARRIER_LAYOUT)desc.LayoutAfter, LayoutAfter = (D3D12_BARRIER_LAYOUT)desc.LayoutAfter,
pResource = resource, pResource = resource,
Subresources = new D3D12_BARRIER_SUBRESOURCE_RANGE Subresources = new D3D12_BARRIER_SUBRESOURCE_RANGE
@@ -272,39 +294,44 @@ internal unsafe class D3D12CommandBuffer : D3D12Object<ID3D12GraphicsCommandList
var groups = stackalloc D3D12_BARRIER_GROUP[3]; var groups = stackalloc D3D12_BARRIER_GROUP[3];
var groupCount = 0u; var groupCount = 0u;
if (globalCount > 0) if (globalIndex > 0)
{ {
groups[groupCount] = new D3D12_BARRIER_GROUP groups[groupCount] = new D3D12_BARRIER_GROUP
{ {
Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_GLOBAL, Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_GLOBAL,
NumBarriers = (uint)globalCount, NumBarriers = (uint)globalIndex,
}; };
groups[groupCount].Anonymous.pGlobalBarriers = pGlobalBarriers; groups[groupCount].Anonymous.pGlobalBarriers = pGlobalBarriers;
groupCount++; groupCount++;
} }
if (bufferCount > 0) if (bufferIndex > 0)
{ {
groups[groupCount] = new D3D12_BARRIER_GROUP groups[groupCount] = new D3D12_BARRIER_GROUP
{ {
Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_BUFFER, Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_BUFFER,
NumBarriers = (uint)bufferCount, NumBarriers = (uint)bufferIndex,
}; };
groups[groupCount].Anonymous.pBufferBarriers = pBufferBarriers; groups[groupCount].Anonymous.pBufferBarriers = pBufferBarriers;
groupCount++; groupCount++;
} }
if (textureCount > 0) if (textureIndex > 0)
{ {
groups[groupCount] = new D3D12_BARRIER_GROUP groups[groupCount] = new D3D12_BARRIER_GROUP
{ {
Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_TEXTURE, Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_TEXTURE,
NumBarriers = (uint)textureCount, NumBarriers = (uint)textureIndex,
}; };
groups[groupCount].Anonymous.pTextureBarriers = pTextureBarriers; groups[groupCount].Anonymous.pTextureBarriers = pTextureBarriers;
groupCount++; groupCount++;
} }
if (groupCount == 0)
{
return;
}
pNativeObject->Barrier(groupCount, groups); pNativeObject->Barrier(groupCount, groups);
} }

View File

@@ -140,7 +140,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
fs.Write(buffer.AsSpan()); fs.Write(buffer.AsSpan());
} }
private static Result<CBufferInfo> ValidateReflectionData(ShaderReflectionData reflectionData) private static Result ValidateReflectionData(ShaderReflectionData reflectionData)
{ {
if (reflectionData.ResourcesBindings.Count > RootSignatureLayout.ROOT_PARAMETER_COUNT) if (reflectionData.ResourcesBindings.Count > RootSignatureLayout.ROOT_PARAMETER_COUNT)
{ {
@@ -149,7 +149,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
if (reflectionData.ResourcesBindings.Count == 0) if (reflectionData.ResourcesBindings.Count == 0)
{ {
return Result.Success(default(CBufferInfo)); return Result.Success();
} }
var rootConstant = reflectionData.ResourcesBindings[0]; var rootConstant = reflectionData.ResourcesBindings[0];
@@ -163,16 +163,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
return Result.Failure($"Root constant buffer size must be {sizeof(PushConstantsData)} bytes."); return Result.Failure($"Root constant buffer size must be {sizeof(PushConstantsData)} bytes.");
} }
var cbufferInfo = new CBufferInfo return Result.Success();
{
Name = rootConstant.Name,
RegisterSlot = rootConstant.BindPoint,
RegisterSpace = rootConstant.Space,
SizeInBytes = rootConstant.Size,
Properties = rootConstant.Properties
};
return Result.Success(cbufferInfo);
} }
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTest ztest, ZWrite zwrite) private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTest ztest, ZWrite zwrite)
@@ -185,7 +176,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
public Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled) public Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled)
{ {
static Result<CBufferInfo> ValidatePassReflectionData(ref readonly GraphicsCompiledResult compiled) static Result ValidatePassReflectionData(ref readonly GraphicsCompiledResult compiled)
{ {
var msr = ValidateReflectionData(compiled.msResult.reflectionData); var msr = ValidateReflectionData(compiled.msResult.reflectionData);
if (msr.IsFailure) if (msr.IsFailure)
@@ -199,12 +190,6 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
return Result.Failure("Validation of pixel shader reflection data failed: " + psr.Message); return Result.Failure("Validation of pixel shader reflection data failed: " + psr.Message);
} }
if (msr.Value.Properties != null
&& msr.Value.SizeInBytes != psr.Value.SizeInBytes)
{
return Result.Failure("Mesh shader and pixel shader constant buffer layouts do not match.");
}
if (compiled.tsResult.IsCreated) if (compiled.tsResult.IsCreated)
{ {
var tsr = ValidateReflectionData(compiled.tsResult.reflectionData); var tsr = ValidateReflectionData(compiled.tsResult.reflectionData);
@@ -212,16 +197,9 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
{ {
return Result.Failure("Validation of task shader reflection data failed: " + tsr.Message); return Result.Failure("Validation of task shader reflection data failed: " + tsr.Message);
} }
if (tsr.Value.Properties != null
&& tsr.Value.SizeInBytes != psr.Value.SizeInBytes)
{
return Result.Failure("Task shader and pixel shader constant buffer layouts do not match.");
}
} }
// ts and ms may not use per material cbuffer at all, so we return the psr value. return Result.Success();
return psr.Value;
} }
AssertNotDisposed(); AssertNotDisposed();
@@ -236,11 +214,11 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
if (!_pipelineCache.ContainsKey(pipelineKey)) if (!_pipelineCache.ContainsKey(pipelineKey))
{ {
//var result = ValidatePassReflectionData(in compiled); var result = ValidatePassReflectionData(in compiled);
//if (result.IsFailure) if (result.IsFailure)
//{ {
// return Result.Failure(result.Message); return result;
//} }
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
{ {

View File

@@ -437,17 +437,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator
return uavDesc; return uavDesc;
} }
private static D3D12_HEAP_TYPE ConvertMemoryType(ResourceMemoryType memoryType)
{
return memoryType switch
{
ResourceMemoryType.Default => D3D12_HEAP_TYPE_DEFAULT,
ResourceMemoryType.Upload => D3D12_HEAP_TYPE_UPLOAD,
ResourceMemoryType.Readback => D3D12_HEAP_TYPE_READBACK,
_ => throw new ArgumentException($"Unsupported memory type: {memoryType}")
};
}
} }
internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
@@ -658,7 +647,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
Handle<GPUResource> resource; Handle<GPUResource> resource;
if (isSubAllocation) if (isSubAllocation)
{ {
resource = _resourceDatabase.ImportExternalResource(pResource, barrierData, resourceDescriptor, name); resource = _resourceDatabase.ImportExternalResource(pResource, barrierData, resourceDescriptor, ResourceDesc.Texture(desc), name);
} }
else else
{ {
@@ -686,7 +675,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
var allocationDesc = new D3D12MA_ALLOCATION_DESC var allocationDesc = new D3D12MA_ALLOCATION_DESC
{ {
HeapType = ConvertMemoryType(desc.MemoryType), HeapType = desc.HeapType.ToD3D12HeapType(),
Flags = D3D12MA_ALLOCATION_FLAG_NONE, Flags = D3D12MA_ALLOCATION_FLAG_NONE,
}; };
@@ -695,11 +684,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
ID3D12Resource* pResource = default; ID3D12Resource* pResource = default;
HRESULT hr; HRESULT hr;
var initialState = desc.MemoryType switch var initialState = desc.HeapType switch
{ {
ResourceMemoryType.Default => D3D12_RESOURCE_STATE_COMMON, HeapType.Default => D3D12_RESOURCE_STATE_COMMON,
ResourceMemoryType.Upload => D3D12_RESOURCE_STATE_GENERIC_READ, HeapType.Upload => D3D12_RESOURCE_STATE_GENERIC_READ,
ResourceMemoryType.Readback => D3D12_RESOURCE_STATE_COPY_DEST, HeapType.Readback => D3D12_RESOURCE_STATE_COPY_DEST,
_ => D3D12_RESOURCE_STATE_COMMON _ => D3D12_RESOURCE_STATE_COMMON
}; };
@@ -770,7 +759,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
Handle<GPUResource> resource; Handle<GPUResource> resource;
if (isSubAllocation) if (isSubAllocation)
{ {
resource = _resourceDatabase.ImportExternalResource(pResource, barrierData, resourceDescriptor, name); resource = _resourceDatabase.ImportExternalResource(pResource, barrierData, resourceDescriptor, ResourceDesc.Buffer(desc), name);
} }
else else
{ {

View File

@@ -45,24 +45,24 @@ internal unsafe class D3D12ResourceDatabase : IResourceDatabase
public readonly bool Allocated => isExternal ? resource.resource.Get() != null : resource.allocation.Get() != null; public readonly bool Allocated => isExternal ? resource.resource.Get() != null : resource.allocation.Get() != null;
public readonly SharedPtr<ID3D12Resource> ResourcePtr => isExternal ? resource.resource.Get() : resource.allocation.Get()->GetResource(); public readonly SharedPtr<ID3D12Resource> ResourcePtr => isExternal ? resource.resource.Get() : resource.allocation.Get()->GetResource();
public ResourceRecord(D3D12MA_Allocation* allocation, ResourceBarrierData barrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc) public ResourceRecord(D3D12MA_Allocation* allocation, ResourceBarrierData barrierData, ResourceViewGroup viewGroup, ResourceDesc desc)
{ {
this.resource = new ResourceUnion(allocation); this.resource = new ResourceUnion(allocation);
this.isExternal = false; this.isExternal = false;
this.viewGroup = resourceDescriptor; this.viewGroup = viewGroup;
this.barrierData = barrierData; this.barrierData = barrierData;
this.desc = desc; this.desc = desc;
} }
public ResourceRecord(ID3D12Resource* resource, ResourceBarrierData barrierData, ResourceViewGroup viewGroup) public ResourceRecord(ID3D12Resource* resource, ResourceBarrierData barrierData, ResourceViewGroup viewGroup, ResourceDesc desc)
{ {
this.resource = new ResourceUnion(resource); this.resource = new ResourceUnion(resource);
this.isExternal = true; this.isExternal = true;
this.viewGroup = viewGroup; this.viewGroup = viewGroup;
this.barrierData = barrierData; this.barrierData = barrierData;
this.desc = resource->GetDesc().ToResourceDesc(); this.desc = desc;
} }
public readonly uint Release(D3D12DescriptorAllocator descriptorAllocator) public readonly uint Release(D3D12DescriptorAllocator descriptorAllocator)
@@ -112,6 +112,8 @@ internal unsafe class D3D12ResourceDatabase : IResourceDatabase
private ulong _cpuFrame; private ulong _cpuFrame;
private bool _disposed; private bool _disposed;
public ResourceBarrierData globalBarrier;
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator) public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
{ {
_descriptorAllocator = descriptorAllocator; _descriptorAllocator = descriptorAllocator;
@@ -130,7 +132,7 @@ internal unsafe class D3D12ResourceDatabase : IResourceDatabase
Dispose(); Dispose();
} }
internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null) internal Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, ResourceDesc desc, string? name = null)
{ {
Debug.Assert(!_disposed); Debug.Assert(!_disposed);
@@ -150,7 +152,7 @@ internal unsafe class D3D12ResourceDatabase : IResourceDatabase
try try
{ {
var id = _resources.Add(new ResourceRecord(pResource, initialBarrierData, viewGroup), out var generation); var id = _resources.Add(new ResourceRecord(pResource, initialBarrierData, viewGroup, desc), out var generation);
var handle = new Handle<GPUResource>(id, generation); var handle = new Handle<GPUResource>(id, generation);
#if DEBUG || GHOST_EDITOR #if DEBUG || GHOST_EDITOR
@@ -169,7 +171,7 @@ internal unsafe class D3D12ResourceDatabase : IResourceDatabase
} }
} }
internal unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null) internal Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
{ {
Debug.Assert(!_disposed); Debug.Assert(!_disposed);
@@ -399,7 +401,7 @@ internal unsafe class D3D12ResourceDatabase : IResourceDatabase
return Error.None; return Error.None;
} }
public Error Map(Handle<GPUResource> handle, uint subResource, ResourceRange? readRange, ResourceRange? writeRange, void* pData, nuint size) public Error MapResource(Handle<GPUResource> handle, uint subResource, ResourceRange? readRange, ResourceRange? writeRange, void* pData, nuint size)
{ {
var r = GetResourceRecord(handle); var r = GetResourceRecord(handle);
if (r.IsFailure) if (r.IsFailure)

View File

@@ -160,7 +160,7 @@ internal unsafe class DXGISwapChain : ISwapChain
sync = BarrierSync.None, sync = BarrierSync.None,
}; };
var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, barrierData, view); var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, barrierData, view, D3D12Utility.GetResourceDesc(pBackBuffer, view));
_backBuffers[i] = handle.AsTexture(); _backBuffers[i] = handle.AsTexture();
} }
} }

View File

@@ -1,4 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.D3D12; namespace Ghost.Graphics.D3D12;
@@ -25,4 +26,52 @@ internal struct ResourceViewGroup
uav = Identifier<CbvSrvUavDescriptor>.Invalid, uav = Identifier<CbvSrvUavDescriptor>.Invalid,
sampler = Identifier<SamplerDescriptor>.Invalid, sampler = Identifier<SamplerDescriptor>.Invalid,
}; };
public readonly TextureUsage GetTextureUsage()
{
var usage = TextureUsage.None;
if (rtv.IsValid)
{
usage |= TextureUsage.RenderTarget;
}
if (dsv.IsValid)
{
usage |= TextureUsage.DepthStencil;
}
if (srv.IsValid)
{
usage |= TextureUsage.ShaderResource;
}
if (uav.IsValid)
{
usage |= TextureUsage.UnorderedAccess;
}
return usage;
}
public readonly BufferUsage GetBufferUsage()
{
var usage = BufferUsage.None;
if (cbv.IsValid)
{
usage |= BufferUsage.Constant;
}
if (srv.IsValid)
{
usage |= BufferUsage.ShaderResource;
}
if (uav.IsValid)
{
usage |= BufferUsage.UnorderedAccess;
}
return usage;
}
} }

View File

@@ -397,6 +397,17 @@ internal static unsafe class D3D12Utility
}; };
} }
public static HeapType ToHeapType(this D3D12_HEAP_TYPE heapType)
{
return heapType switch
{
D3D12_HEAP_TYPE_DEFAULT => HeapType.Default,
D3D12_HEAP_TYPE_UPLOAD => HeapType.Upload,
D3D12_HEAP_TYPE_READBACK => HeapType.Readback,
_ => throw new ArgumentException($"Unknown D3D12 heap type: {heapType}")
};
}
public static D3D12_HEAP_FLAGS ToD3D12HeapFlags(this HeapFlags flags) public static D3D12_HEAP_FLAGS ToD3D12HeapFlags(this HeapFlags flags)
{ {
return flags switch return flags switch
@@ -411,6 +422,24 @@ internal static unsafe class D3D12Utility
}; };
} }
public static HeapFlags ToHeapFlags(this D3D12_HEAP_FLAGS flags)
{
if (flags == D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES)
{
return HeapFlags.AllowAllBufferAndTexture;
}
return flags switch
{
D3D12_HEAP_FLAG_NONE => HeapFlags.None,
D3D12_HEAP_FLAG_SHARED => HeapFlags.Shared,
D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS => HeapFlags.AllowOnlyBuffers,
D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES => HeapFlags.AllowOnlyTextures,
D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES => HeapFlags.AllowOnlyRTAndDS,
_ => throw new ArgumentException($"Unknown D3D12 heap flags: {flags}")
};
}
public static D3D12_RESOURCE_DESC ToD3D12ResourceDesc(this in TextureDesc desc) public static D3D12_RESOURCE_DESC ToD3D12ResourceDesc(this in TextureDesc desc)
{ {
var dxgiFormat = desc.Format.ToDXGIFormat(); var dxgiFormat = desc.Format.ToDXGIFormat();
@@ -497,17 +526,22 @@ internal static unsafe class D3D12Utility
return D3D12_RESOURCE_DESC.Buffer(alignedSize, resourceFlags); return D3D12_RESOURCE_DESC.Buffer(alignedSize, resourceFlags);
} }
public static ResourceDesc GetResourceDesc(ID3D12Resource* pResource, ResourceViewGroup viewGroup)
public static ResourceDesc ToResourceDesc(this D3D12_RESOURCE_DESC desc)
{ {
D3D12_HEAP_PROPERTIES heapProperties;
D3D12_HEAP_FLAGS heapFlags;
ThrowIfFailed(pResource->GetHeapProperties(&heapProperties, &heapFlags));
var desc = pResource->GetDesc();
if (desc.Dimension == D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_BUFFER) if (desc.Dimension == D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_BUFFER)
{ {
return ResourceDesc.Buffer(new BufferDesc return ResourceDesc.Buffer(new BufferDesc
{ {
Size = (uint)desc.Width, Size = (uint)desc.Width,
Stride = 0, Stride = 0,
Usage = BufferUsage.None, Usage = viewGroup.GetBufferUsage(),
MemoryType = ResourceMemoryType.Default HeapType = heapProperties.Type.ToHeapType()
}); });
} }
else else
@@ -520,7 +554,7 @@ internal static unsafe class D3D12Utility
Format = desc.Format.ToTextureFormat(), Format = desc.Format.ToTextureFormat(),
Dimension = desc.Dimension.ToTextureDimension(), Dimension = desc.Dimension.ToTextureDimension(),
MipLevels = desc.MipLevels, MipLevels = desc.MipLevels,
Usage = TextureUsage.None, Usage = viewGroup.GetTextureUsage(),
}); });
} }
} }

View File

@@ -556,31 +556,16 @@ public struct BarrierDesc
get; set; get; set;
} }
public BarrierSync? SyncBefore
{
get; set;
}
public BarrierSync SyncAfter public BarrierSync SyncAfter
{ {
get; set; get; set;
} }
public BarrierAccess? AccessBefore
{
get; set;
}
public BarrierAccess AccessAfter public BarrierAccess AccessAfter
{ {
get; set; get; set;
} }
public BarrierLayout? LayoutBefore
{
get; set;
}
public BarrierLayout LayoutAfter public BarrierLayout LayoutAfter
{ {
get; set; get; set;
@@ -601,45 +586,45 @@ public struct BarrierDesc
get; set; get; set;
} }
public static BarrierDesc Global(BarrierSync syncBefore, BarrierSync syncAfter, BarrierAccess accessBefore, BarrierAccess accessAfter) public bool IsAliasing
{
get; set;
}
public static BarrierDesc Global(BarrierSync syncAfter, BarrierAccess accessAfter)
{ {
return new BarrierDesc return new BarrierDesc
{ {
Type = BarrierType.Global, Type = BarrierType.Global,
SyncBefore = syncBefore,
SyncAfter = syncAfter, SyncAfter = syncAfter,
AccessBefore = accessBefore,
AccessAfter = accessAfter AccessAfter = accessAfter
}; };
} }
public static BarrierDesc Buffer(Handle<GPUResource> resource, BarrierSync? syncBefore, BarrierSync syncAfter, BarrierAccess? accessBefore, BarrierAccess accessAfter) public static BarrierDesc Buffer(Handle<GPUResource> resource, BarrierSync syncAfter, BarrierAccess accessAfter, bool isAliasing = false)
{ {
return new BarrierDesc return new BarrierDesc
{ {
Type = BarrierType.Buffer, Type = BarrierType.Buffer,
Resource = resource, Resource = resource,
SyncBefore = syncBefore,
SyncAfter = syncAfter, SyncAfter = syncAfter,
AccessBefore = accessBefore, AccessAfter = accessAfter,
AccessAfter = accessAfter IsAliasing = isAliasing
}; };
} }
public static BarrierDesc Texture(Handle<GPUResource> resource, BarrierSync? syncBefore, BarrierSync syncAfter, BarrierAccess? accessBefore, BarrierAccess accessAfter, BarrierLayout? layoutBefore, BarrierLayout layoutAfter, BarrierSubresourceRange subresources = default, bool discard = false) public static BarrierDesc Texture(Handle<GPUResource> resource, BarrierSync syncAfter, BarrierAccess accessAfter, BarrierLayout layoutAfter, BarrierSubresourceRange subresources = default, bool discard = false, bool isAliasing = false)
{ {
return new BarrierDesc return new BarrierDesc
{ {
Type = BarrierType.Texture, Type = BarrierType.Texture,
Resource = resource, Resource = resource,
SyncBefore = syncBefore,
SyncAfter = syncAfter, SyncAfter = syncAfter,
AccessBefore = accessBefore,
AccessAfter = accessAfter, AccessAfter = accessAfter,
LayoutBefore = layoutBefore,
LayoutAfter = layoutAfter, LayoutAfter = layoutAfter,
Subresources = subresources, Subresources = subresources,
Discard = discard Discard = discard,
IsAliasing = isAliasing
}; };
} }
} }
@@ -724,6 +709,16 @@ public record struct ResourceDesc
_ => throw new InvalidOperationException($"Unknown resource type: {Type}") _ => throw new InvalidOperationException($"Unknown resource type: {Type}")
}; };
} }
public override string ToString()
{
return Type switch
{
ResourceType.Texture => $"Texture: {TextureDescription}",
ResourceType.Buffer => $"Buffer: {BufferDescription}",
_ => $"Unknown resource type: {Type}"
};
}
} }
/// <summary> /// <summary>
@@ -998,7 +993,7 @@ public record struct BufferDesc
get; set; get; set;
} }
public ResourceMemoryType MemoryType public HeapType HeapType
{ {
get; set; get; set;
} }
@@ -1255,13 +1250,6 @@ public enum RenderTargetCreationFlags
GenerateMips = 1 << 3 GenerateMips = 1 << 3
} }
public enum ResourceMemoryType
{
Default, // GPU memory
Upload, // CPU-to-GPU memory
Readback // GPU-to-CPU memory
}
public enum ResourceType public enum ResourceType
{ {
Texture, Texture,

View File

@@ -126,7 +126,7 @@ public unsafe interface IResourceDatabase : IDisposable
/// <returns>An Error indicating the success or failure of the swap operation.</returns> /// <returns>An Error indicating the success or failure of the swap operation.</returns>
Error Swap(Handle<GPUResource> handleA, Handle<GPUResource> handleB); Error Swap(Handle<GPUResource> handleA, Handle<GPUResource> handleB);
Error Map(Handle<GPUResource> handle, uint subResource, ResourceRange? readRange, ResourceRange? writeRange, void* pData, nuint size); Error MapResource(Handle<GPUResource> handle, uint subResource, ResourceRange? readRange, ResourceRange? writeRange, void* pData, nuint size);
ulong GetIntermediateResourceSize(Handle<GPUResource> resource, uint firstSubResource, uint numSubResources); ulong GetIntermediateResourceSize(Handle<GPUResource> resource, uint firstSubResource, uint numSubResources);
} }

View File

@@ -117,7 +117,7 @@ public struct Material : IResourceReleasable
{ {
Size = shader.CBufferSize, Size = shader.CBufferSize,
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
var buffer = resourceAllocator.CreateBuffer(ref desc, "MaterialCBuffer"); var buffer = resourceAllocator.CreateBuffer(ref desc, "MaterialCBuffer");
@@ -243,7 +243,7 @@ public struct Material : IResourceReleasable
return _keywordMask.IsKeywordEnabled(localIndex); return _keywordMask.IsKeywordEnabled(localIndex);
} }
public readonly void UploadData(ICommandBuffer cmd, IResourceDatabase resourceDatabase) public readonly void UploadData(RenderContext ctx)
{ {
if (!_isDirty) if (!_isDirty)
{ {
@@ -251,31 +251,20 @@ public struct Material : IResourceReleasable
} }
var cbufferResource = _cBufferCache.GpuResource.AsResource(); var cbufferResource = _cBufferCache.GpuResource.AsResource();
var r = resourceDatabase.GetResourceBarrierData(cbufferResource);
if (r.IsFailure)
{
return;
}
var barrierData = r.Value;
var desc = BarrierDesc.Buffer( var desc = BarrierDesc.Buffer(
cbufferResource, cbufferResource,
barrierData.sync,
BarrierSync.Copy, BarrierSync.Copy,
barrierData.access,
BarrierAccess.CopyDest); BarrierAccess.CopyDest);
cmd.Barrier(desc); ctx.CommandBuffer.Barrier(desc);
cmd.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan()); ctx.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan());
desc = BarrierDesc.Buffer( desc = BarrierDesc.Buffer(
cbufferResource, cbufferResource,
BarrierSync.Copy,
BarrierSync.AllShading, BarrierSync.AllShading,
BarrierAccess.CopyDest,
BarrierAccess.ShaderResource); BarrierAccess.ShaderResource);
cmd.Barrier(desc); ctx.CommandBuffer.Barrier(desc);
} }
public void ReleaseResource(IResourceDatabase database) public void ReleaseResource(IResourceDatabase database)

View File

@@ -7,13 +7,13 @@ using System.Diagnostics;
namespace Ghost.Graphics.Core; namespace Ghost.Graphics.Core;
// TODO: Temporary rendering context for heap creation and data upload. We will refactor it later when we have a better understanding of the engine architecture. // TODO: Temporary rendering context for heap creation and data upload. We will refactor it later when we have a better understanding of the engine architecture.
public readonly unsafe ref struct RenderingContext public readonly unsafe ref struct RenderContext
{ {
private readonly IGraphicsEngine _engine; private readonly IGraphicsEngine _engine;
private readonly ResourceManager _resourceManager; private readonly ResourceManager _resourceManager;
private readonly ICommandBuffer _directCmd; private readonly ICommandBuffer _cmd;
public ICommandBuffer DirectCommandBuffer => _directCmd; public ICommandBuffer CommandBuffer => _cmd;
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler; public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
public ResourceManager ResourceManager => _resourceManager; public ResourceManager ResourceManager => _resourceManager;
@@ -21,69 +21,29 @@ public readonly unsafe ref struct RenderingContext
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase; public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary; public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
internal RenderingContext(IGraphicsEngine engine, ResourceManager resourceManager, ICommandBuffer directCmd) internal RenderContext(IGraphicsEngine engine, ResourceManager resourceManager, ICommandBuffer cmd)
{ {
_engine = engine; _engine = engine;
_resourceManager = resourceManager; _resourceManager = resourceManager;
_directCmd = directCmd; _cmd = cmd;
}
public ICommandBuffer CrearteCommandBuffer(CommandBufferType type)
{
return _engine.CreateCommandBuffer(type);
}
// TODO: ExecuteCommandBufferAsync with fencene.Device.GraphicsQueue.Submit(commandBuffer);
public void ExecuteCommandBuffer(ICommandBuffer commandBuffer)
{
var queue = commandBuffer.Type switch
{
CommandBufferType.Graphics => _engine.Device.GraphicsQueue,
CommandBufferType.Compute => _engine.Device.ComputeQueue,
CommandBufferType.Copy => _engine.Device.CopyQueue,
_ => throw new InvalidOperationException("Unknown command buffer type."),
};
queue.Submit(commandBuffer);
queue.WaitIdle();
} }
private void TransitionBarrier(Handle<GPUResource> resource, bool isTexture, BarrierLayout newLayout, BarrierAccess newAccess, BarrierSync newSync) private void TransitionBarrier(Handle<GPUResource> resource, bool isTexture, BarrierLayout newLayout, BarrierAccess newAccess, BarrierSync newSync)
{ {
var r = ResourceDatabase.GetResourceBarrierData(resource);
if (r.IsFailure)
{
return;
}
var data = r.Value;
if (data.layout == newLayout && data.access == newAccess && data.sync == newSync)
{
return;
}
BarrierDesc desc; BarrierDesc desc;
if (isTexture) if (isTexture)
{ {
desc = BarrierDesc.Texture( desc = BarrierDesc.Texture(resource, newSync, newAccess, newLayout);
resource,
data.sync, newSync,
data.access, newAccess,
data.layout, newLayout);
} }
else else
{ {
desc = BarrierDesc.Buffer( desc = BarrierDesc.Buffer(resource, newSync, newAccess);
resource,
data.sync, newSync,
data.access, newAccess);
} }
_directCmd.Barrier(new ReadOnlySpan<BarrierDesc>(in desc)); _cmd.Barrier(desc);
ResourceDatabase.SetResourceBarrierData(resource, new ResourceBarrierData(newLayout, newAccess, newSync));
} }
private void UploadBuffer<T>(Handle<GPUBuffer> buffer, params ReadOnlySpan<T> data) public void UploadBuffer<T>(Handle<GPUBuffer> buffer, params ReadOnlySpan<T> data)
where T : unmanaged where T : unmanaged
{ {
var r = _engine.ResourceDatabase.GetResourceDescription(buffer.AsResource()); var r = _engine.ResourceDatabase.GetResourceDescription(buffer.AsResource());
@@ -95,34 +55,43 @@ public readonly unsafe ref struct RenderingContext
Debug.Assert(r.Value.Type == ResourceType.Buffer); Debug.Assert(r.Value.Type == ResourceType.Buffer);
var sizeInBytes = (nuint)(data.Length * sizeof(T)); var sizeInBytes = (nuint)(data.Length * sizeof(T));
var memoryType = r.Value.BufferDescription.MemoryType; var memoryType = r.Value.BufferDescription.HeapType;
if (memoryType == ResourceMemoryType.Upload) if (memoryType == HeapType.Upload)
{ {
fixed (T* pData = data) fixed (T* pData = data)
{ {
ResourceDatabase.Map(buffer.AsResource(), 0, null, null, pData, sizeInBytes); ResourceDatabase.MapResource(buffer.AsResource(), 0, null, null, pData, sizeInBytes);
} }
} }
else else
{ {
//var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
//var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
var uploadDesc = new BufferDesc var uploadDesc = new BufferDesc
{ {
Size = sizeInBytes, Size = sizeInBytes,
Usage = BufferUsage.Upload, Usage = BufferUsage.Upload,
MemoryType = ResourceMemoryType.Upload, HeapType = HeapType.Upload,
}; };
var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc); var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc);
if (uploadHandle.IsInvalid)
fixed (T* pData = data)
{ {
ResourceDatabase.Map(uploadHandle.AsResource(), 0, null, null, pData, sizeInBytes); throw new OutOfMemoryException("Failed to create upload buffer for buffer data.");
} }
_directCmd.CopyBuffer(buffer, uploadHandle, 0, 0, sizeInBytes); try
{
fixed (T* pData = data)
{
ResourceDatabase.MapResource(uploadHandle.AsResource(), 0, null, null, pData, sizeInBytes);
}
_cmd.CopyBuffer(buffer, uploadHandle, 0, 0, sizeInBytes);
}
finally
{
ResourceDatabase.ReleaseResource(uploadHandle.AsResource());
}
} }
} }
@@ -169,8 +138,6 @@ public readonly unsafe ref struct RenderingContext
return CreateMesh(vertexList, indexList, staticMesh); return CreateMesh(vertexList, indexList, staticMesh);
} }
// TODO: Make one memory pool for upload.
/// <summary> /// <summary>
/// Uploads the mesh data to the GPU. /// Uploads the mesh data to the GPU.
/// </summary> /// </summary>
@@ -221,14 +188,14 @@ public readonly unsafe ref struct RenderingContext
Size = (uint)(meshletData.meshlets.Count * sizeof(Meshlet)), Size = (uint)(meshletData.meshlets.Count * sizeof(Meshlet)),
Stride = (uint)sizeof(Meshlet), Stride = (uint)sizeof(Meshlet),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
var verticesDesc = new BufferDesc var verticesDesc = new BufferDesc
{ {
Size = (uint)(meshletData.meshletVertices.Count * sizeof(uint)), Size = (uint)(meshletData.meshletVertices.Count * sizeof(uint)),
Stride = sizeof(uint), Stride = sizeof(uint),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
// Ensure size is multiple of 4 for Raw buffer // Ensure size is multiple of 4 for Raw buffer
var trianglesSize = (uint)meshletData.meshletTriangles.Count * sizeof(uint); var trianglesSize = (uint)meshletData.meshletTriangles.Count * sizeof(uint);
@@ -237,7 +204,7 @@ public readonly unsafe ref struct RenderingContext
Size = trianglesSize, Size = trianglesSize,
Stride = sizeof(uint), Stride = sizeof(uint),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
meshRef.MeshLetBuffer = _engine.ResourceAllocator.CreateBuffer(in meshletDesc, "Meshlets"); meshRef.MeshLetBuffer = _engine.ResourceAllocator.CreateBuffer(in meshletDesc, "Meshlets");
@@ -296,12 +263,6 @@ public readonly unsafe ref struct RenderingContext
public void UploadTexture<T>(Handle<GPUTexture> texture, ReadOnlySpan<T> data) public void UploadTexture<T>(Handle<GPUTexture> texture, ReadOnlySpan<T> data)
where T : unmanaged where T : unmanaged
{ {
//var size = ResourceAllocator.GetSizeInfo(desc).Size;
//if ((ulong)(data.Length * sizeof(T)) != ResourceAllocator.GetSizeInfo(desc).Size)
//{
// throw new ArgumentException("Data size does not match texture size.");
//}
var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()).GetValueOrThrow(); var desc = ResourceDatabase.GetResourceDescription(texture.AsResource()).GetValueOrThrow();
desc.TextureDescription.Format.GetSurfaceInfo(desc.TextureDescription.Width, desc.TextureDescription.Height, out var rowPitch, out var slicePitch, out _); desc.TextureDescription.Format.GetSurfaceInfo(desc.TextureDescription.Width, desc.TextureDescription.Height, out var rowPitch, out var slicePitch, out _);
@@ -310,7 +271,7 @@ public readonly unsafe ref struct RenderingContext
{ {
Size = requiredSize, Size = requiredSize,
Usage = BufferUsage.Upload, Usage = BufferUsage.Upload,
MemoryType = ResourceMemoryType.Upload, HeapType = HeapType.Upload,
}; };
var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc); var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc);
@@ -319,6 +280,8 @@ public readonly unsafe ref struct RenderingContext
throw new OutOfMemoryException("Failed to create upload buffer for texture data."); throw new OutOfMemoryException("Failed to create upload buffer for texture data.");
} }
try
{
TransitionBarrier(texture.AsResource(), true, BarrierLayout.CopyDest, BarrierAccess.CopyDest, BarrierSync.Copy); TransitionBarrier(texture.AsResource(), true, BarrierLayout.CopyDest, BarrierAccess.CopyDest, BarrierSync.Copy);
fixed (T* pData = data) fixed (T* pData = data)
@@ -330,7 +293,12 @@ public readonly unsafe ref struct RenderingContext
slicePitch = slicePitch slicePitch = slicePitch
}; };
_directCmd.UpdateSubResources(texture.AsResource(), uploadHandle.AsResource(), subresourceData); _cmd.UpdateSubResources(texture.AsResource(), uploadHandle.AsResource(), subresourceData);
}
}
finally
{
ResourceDatabase.ReleaseResource(uploadHandle.AsResource());
} }
} }
} }

View File

@@ -1,94 +0,0 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
namespace Ghost.Graphics.Core;
internal class SwapChainRenderOutput : IRenderOutput
{
private readonly ISwapChain _swapChain;
public ViewportDesc Viewport
{
get; set;
}
public ScissorRectDesc Scissor
{
get; set;
}
public SwapChainRenderOutput(ISwapChain swapChain)
{
_swapChain = swapChain;
Viewport = new ViewportDesc { Width = swapChain.Width, Height = swapChain.Height, MinDepth = 0, MaxDepth = 1 };
Scissor = new ScissorRectDesc { Right = swapChain.Width, Bottom = swapChain.Height };
}
public Handle<GPUTexture> GetRenderTarget()
{
return _swapChain.GetCurrentBackBuffer();
}
public void BeginRender(ICommandBuffer cmd)
{
var barrierDesc = BarrierDesc.Texture(_swapChain.GetCurrentBackBuffer().AsResource(),
BarrierSync.None, BarrierSync.RenderTarget,
BarrierAccess.NoAccess, BarrierAccess.RenderTarget,
BarrierLayout.Present, BarrierLayout.RenderTarget);
cmd.Barrier(barrierDesc);
}
public void EndRender(ICommandBuffer cmd)
{
var barrierDesc = BarrierDesc.Texture(_swapChain.GetCurrentBackBuffer().AsResource(),
BarrierSync.RenderTarget, BarrierSync.None,
BarrierAccess.RenderTarget, BarrierAccess.NoAccess,
BarrierLayout.RenderTarget, BarrierLayout.Present);
cmd.Barrier(barrierDesc);
}
public void Present()
{
_swapChain.Present();
}
}
internal class TextureRenderOutput : IRenderOutput
{
private readonly Handle<GPUTexture> _texture;
public ViewportDesc Viewport
{
get; set;
}
public ScissorRectDesc Scissor
{
get; set;
}
public TextureRenderOutput(Handle<GPUTexture> texture)
{
_texture = texture;
}
public Handle<GPUTexture> GetRenderTarget()
{
return _texture;
}
public void BeginRender(ICommandBuffer cmd)
{
}
public void EndRender(ICommandBuffer cmd)
{
}
public void Present()
{
}
}

View File

@@ -217,6 +217,7 @@ internal sealed class RenderGraphExecutor
} }
// Process all pre-compiled barriers for this pass // Process all pre-compiled barriers for this pass
// TODO: We can insert BarrierAccess.NoAccess to the resource that aliased with others after their last usage to reduce cache burden.
while (barrierIndex < compiledBarriers.Count && compiledBarriers[barrierIndex].PassIndex == passIndex) while (barrierIndex < compiledBarriers.Count && compiledBarriers[barrierIndex].PassIndex == passIndex)
{ {
var compiledBarrier = compiledBarriers[barrierIndex++]; var compiledBarrier = compiledBarriers[barrierIndex++];
@@ -231,60 +232,23 @@ internal sealed class RenderGraphExecutor
} }
var currentState = currentStateResult.Value; var currentState = currentStateResult.Value;
BarrierLayout layoutBefore;
BarrierAccess accessBefore;
BarrierSync syncBefore;
// Handle aliasing barriers specially
if (compiledBarrier.AliasingPredecessor.IsValid)
{
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
var predStateResult = _resourceDatabase.GetResourceBarrierData(predHandle);
if (predStateResult.IsFailure)
{
return predStateResult.Error;
}
var predState = predStateResult.Value;
layoutBefore = BarrierLayout.Undefined;
accessBefore = BarrierAccess.NoAccess;
syncBefore = predState.sync;
}
else
{
layoutBefore = currentState.layout;
accessBefore = currentState.access;
syncBefore = currentState.sync;
}
var target = compiledBarrier.TargetState; var target = compiledBarrier.TargetState;
// Skip if already in target state (optimization)
if (!compiledBarrier.AliasingPredecessor.IsValid &&
layoutBefore == target.layout &&
accessBefore == target.access &&
syncBefore == target.sync)
{
continue;
}
// Create barrier descriptor // Create barrier descriptor
BarrierDesc desc; BarrierDesc desc;
if (compiledBarrier.ResourceType == RenderGraphResourceType.Texture) if (compiledBarrier.ResourceType == RenderGraphResourceType.Texture)
{ {
desc = BarrierDesc.Texture(resourceHandle, desc = BarrierDesc.Texture(resourceHandle, target.sync, target.access, target.layout,
syncBefore, target.sync,
accessBefore, target.access,
layoutBefore, target.layout,
discard: compiledBarrier.Flags.HasFlag(BarrierFlags.Discard)); discard: compiledBarrier.Flags.HasFlag(BarrierFlags.Discard));
} }
else else
{ {
desc = BarrierDesc.Buffer(resourceHandle, desc = BarrierDesc.Buffer(resourceHandle, target.sync, target.access);
syncBefore, target.sync, }
accessBefore, target.access);
if (compiledBarrier.AliasingPredecessor.IsValid)
{
desc.IsAliasing = true;
} }
if (barrierCount >= MaxBatch) if (barrierCount >= MaxBatch)

View File

@@ -1,16 +1,10 @@
using Ghost.Core;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using System.Diagnostics;
namespace Ghost.Graphics.RenderPipeline; namespace Ghost.Graphics.RenderPipeline;
public readonly struct RenderContext
{
public ICommandBuffer CommandBuffer { get; init; }
public ICommandQueue GraphicsQueue { get; init; }
public ICommandQueue ComputeQueue { get; init; }
public ICommandQueue CopyQueue { get; init; }
}
public interface IRenderPipelineSettings public interface IRenderPipelineSettings
{ {
IRenderPipeline CreatePipeline(RenderSystem renderSystem); IRenderPipeline CreatePipeline(RenderSystem renderSystem);

View File

@@ -100,7 +100,8 @@ public class RenderSystem : IDisposable
private uint _frameIndex; private uint _frameIndex;
private ulong _cpuFenceValue; private ulong _cpuFenceValue;
private ulong _gpuFenceValue; private ulong _submittedFenceValue;
private ulong _completedFenceValue;
private bool _isRunning; private bool _isRunning;
private bool _disposed; private bool _disposed;
@@ -112,7 +113,8 @@ public class RenderSystem : IDisposable
public bool IsRunning => _isRunning; public bool IsRunning => _isRunning;
public ulong CPUFenceValue => _cpuFenceValue; public ulong CPUFenceValue => _cpuFenceValue;
public ulong GPUFenceValue => _gpuFenceValue; public ulong SubmittedFenceValue => _submittedFenceValue;
public ulong CompletedFenceValue => _completedFenceValue;
public uint FrameIndex => _frameIndex; public uint FrameIndex => _frameIndex;
public uint MaxFrameLatency => _config.FrameBufferCount; public uint MaxFrameLatency => _config.FrameBufferCount;
@@ -219,7 +221,7 @@ public class RenderSystem : IDisposable
while (_isRunning) while (_isRunning)
{ {
_frameIndex = (uint)(_gpuFenceValue % _config.FrameBufferCount); _frameIndex = (uint)(_submittedFenceValue % _config.FrameBufferCount);
ref var frameResource = ref _frameResources[_frameIndex]; ref var frameResource = ref _frameResources[_frameIndex];
// Wait for either CPU ready signal or shutdown signal // Wait for either CPU ready signal or shutdown signal
@@ -242,17 +244,7 @@ public class RenderSystem : IDisposable
if (!_resizeRequest.IsEmpty) if (!_resizeRequest.IsEmpty)
{ {
_gpuFenceValue++; WaitIdle();
var flushFence = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
_graphicsEngine.Device.GraphicsQueue.WaitForValue(flushFence);
// Sync the current frame heap to this new fence to keep state consistent
frameResource.FenceValue = flushFence;
foreach (var resource in _frameResources)
{
resource.CommandAllocator.Reset();
}
var keys = _resizeRequest.Keys.ToArray(); var keys = _resizeRequest.Keys.ToArray();
foreach (var swapChain in keys) foreach (var swapChain in keys)
@@ -262,17 +254,19 @@ public class RenderSystem : IDisposable
swapChain.Resize(newSize.x, newSize.y); swapChain.Resize(newSize.x, newSize.y);
} }
} }
}
frameResource.GpuReadyEvent.Set(); _completedFenceValue = _graphicsEngine.Device.GraphicsQueue.GetCompletedValue();
if (_submittedFenceValue < _completedFenceValue)
continue; // Skip rendering this frame since we just resized and may have invalid render targets {
_submittedFenceValue = _completedFenceValue;
} }
// Begin rendering for this frame // Begin rendering for this frame
frameResource.CommandAllocator.Reset(); frameResource.CommandAllocator.Reset();
_resourceManager.BeginFrame(_cpuFenceValue); _resourceManager.BeginFrame(_submittedFenceValue + 1);
var r = _graphicsEngine.BeginFrame(_cpuFenceValue); var r = _graphicsEngine.BeginFrame(_submittedFenceValue + 1);
if (r.IsFailure) if (r.IsFailure)
{ {
@@ -290,11 +284,9 @@ public class RenderSystem : IDisposable
{ {
cmd.Begin(frameResource.CommandAllocator); cmd.Begin(frameResource.CommandAllocator);
var renderCtx = new RenderContext var renderCtx = new RenderContext(_graphicsEngine, _resourceManager, cmd);
{
CommandBuffer = cmd
};
//Debug.WriteLine($"GPU: Frame started.");
_renderPipeline.Render(renderCtx, renderRequests.AsSpan()); _renderPipeline.Render(renderCtx, renderRequests.AsSpan());
_swapChainManager.TransitionToPresent(cmd); _swapChainManager.TransitionToPresent(cmd);
@@ -321,9 +313,9 @@ public class RenderSystem : IDisposable
renderRequests.Clear(); renderRequests.Clear();
} }
// End the frame and present // End the frame and retire resources based on actual GPU progress.
_resourceManager.EndFrame(_gpuFenceValue); _resourceManager.EndFrame(_completedFenceValue);
r = _graphicsEngine.EndFrame(_gpuFenceValue); r = _graphicsEngine.EndFrame(_completedFenceValue);
if (r.IsFailure) if (r.IsFailure)
{ {
@@ -331,11 +323,9 @@ public class RenderSystem : IDisposable
break; break;
} }
// Prepare for the next frame. _submittedFenceValue++;
_gpuFenceValue++;
frameResource.GpuReadyEvent.Set(); frameResource.GpuReadyEvent.Set();
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue); frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_submittedFenceValue);
} }
} }
@@ -381,6 +371,21 @@ public class RenderSystem : IDisposable
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize); _resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
} }
internal bool TryAcquireCPUFrame()
{
ulong requiredGpuFence = _cpuFenceValue < _config.FrameBufferCount ? 0 : _cpuFenceValue - _config.FrameBufferCount + 1;
if (requiredGpuFence > 0 && _graphicsEngine.Device.GraphicsQueue.GetCompletedValue() < requiredGpuFence)
{
return false;
}
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
_frameResources[eventIndex].RenderRequests.Clear();
return true;
}
public void AddRenderRequest(in RenderRequest request) public void AddRenderRequest(in RenderRequest request)
{ {
Debug.Assert(!_disposed, "Cannot add render request to a disposed RenderSystem."); Debug.Assert(!_disposed, "Cannot add render request to a disposed RenderSystem.");

View File

@@ -10,6 +10,7 @@ public partial class ResourceManager
{ {
private const ulong _DEFAULT_TRANSIENT_PAGE_SIZE = 16 * 1024 * 1024; // 16MB private const ulong _DEFAULT_TRANSIENT_PAGE_SIZE = 16 * 1024 * 1024; // 16MB
[DebuggerDisplay("Heap: {heap}, Offset: {offset}, HeapType: {heapType}, HeapFlags: {heapFlags}")]
private struct Page private struct Page
{ {
public Handle<GPUResource> heap; public Handle<GPUResource> heap;
@@ -19,6 +20,7 @@ public partial class ResourceManager
public HeapType heapType; public HeapType heapType;
} }
[DebuggerDisplay("Page Heap: {page.heap}, RetireFrame: {retireFrame}")]
private struct RetiringPage private struct RetiringPage
{ {
public Page page; public Page page;
@@ -26,14 +28,14 @@ public partial class ResourceManager
} }
private UnsafeList<Page> _activePages; private UnsafeList<Page> _activePages;
private UnsafeQueue<RetiringPage> _retiringPages; private Queue<RetiringPage> _retiringPages;
private UnsafeList<Handle<GPUResource>> _oversizedTransientResources; private UnsafeList<Handle<GPUResource>> _oversizedTransientResources;
private void InitializePool() private void InitializePool()
{ {
_activePages = new UnsafeList<Page>(4, Allocator.Persistent); _activePages = new UnsafeList<Page>(4, Allocator.Persistent);
_retiringPages = new UnsafeQueue<RetiringPage>(4, Allocator.Persistent); _retiringPages = new Queue<RetiringPage>(4);
_oversizedTransientResources = new UnsafeList<Handle<GPUResource>>(4, Allocator.Persistent); _oversizedTransientResources = new UnsafeList<Handle<GPUResource>>(4, Allocator.Persistent);
} }
@@ -152,10 +154,10 @@ public partial class ResourceManager
return bufHandle; return bufHandle;
} }
var requiredHeapType = desc.MemoryType switch var requiredHeapType = desc.HeapType switch
{ {
ResourceMemoryType.Upload => HeapType.Upload, HeapType.Upload => HeapType.Upload,
ResourceMemoryType.Readback => HeapType.Readback, HeapType.Readback => HeapType.Readback,
_ => HeapType.Default _ => HeapType.Default
}; };
@@ -265,7 +267,7 @@ public partial class ResourceManager
} }
_activePages.Dispose(); _activePages.Dispose();
_retiringPages.Dispose(); //_retiringPages.Dispose();
_oversizedTransientResources.Dispose(); _oversizedTransientResources.Dispose();
} }
} }

View File

@@ -83,7 +83,7 @@ public sealed partial class ResourceManager : IDisposable
Size = (uint)(vertices.Count * sizeof(Vertex)), Size = (uint)(vertices.Count * sizeof(Vertex)),
Stride = (uint)sizeof(Vertex), Stride = (uint)sizeof(Vertex),
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw, Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
var indexBufferDesc = new BufferDesc var indexBufferDesc = new BufferDesc
@@ -91,7 +91,7 @@ public sealed partial class ResourceManager : IDisposable
Size = (uint)(indices.Count * sizeof(uint)), Size = (uint)(indices.Count * sizeof(uint)),
Stride = sizeof(uint), Stride = sizeof(uint),
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw, Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
var objectBufferDesc = new BufferDesc var objectBufferDesc = new BufferDesc
@@ -99,7 +99,7 @@ public sealed partial class ResourceManager : IDisposable
Size = (uint)sizeof(MeshData), Size = (uint)sizeof(MeshData),
Stride = (uint)sizeof(MeshData), Stride = (uint)sizeof(MeshData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Default, HeapType = HeapType.Default,
}; };
var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer"); var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer");

View File

@@ -127,9 +127,9 @@ internal class SwapChainManager : IDisposable
} }
commandBuffer.Barrier(BarrierDesc.Texture(record.SwapChain.GetCurrentBackBuffer().AsResource(), commandBuffer.Barrier(BarrierDesc.Texture(record.SwapChain.GetCurrentBackBuffer().AsResource(),
null, BarrierSync.None, BarrierSync.None,
null, BarrierAccess.NoAccess, BarrierAccess.NoAccess,
null, BarrierLayout.Present)); BarrierLayout.Present));
} }
} }

View File

@@ -1,7 +1,5 @@
using Ghost.Core; using Ghost.Core;
using Ghost.Core.Graphics;
using Ghost.DSL.ShaderCompiler; using Ghost.DSL.ShaderCompiler;
using Ghost.Engine.Components;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RenderGraphModule; using Ghost.Graphics.RenderGraphModule;
using Ghost.Graphics.RenderPipeline; using Ghost.Graphics.RenderPipeline;
@@ -77,9 +75,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
private static float3 IntersectFrustumPlanes(float4 p0, float4 p1, float4 p2) private static float3 IntersectFrustumPlanes(float4 p0, float4 p1, float4 p2)
{ {
float3 n0 = p0.xyz; var n0 = p0.xyz;
float3 n1 = p1.xyz; var n1 = p1.xyz;
float3 n2 = p2.xyz; var n2 = p2.xyz;
float det = math.dot(math.cross(n0, n1), n2); float det = math.dot(math.cross(n0, n1), n2);
return (math.cross(n2, n1) * p0.w + math.cross(n0, n2) * p1.w - math.cross(n0, n1) * p2.w) * (1.0f / det); return (math.cross(n2, n1) * p0.w + math.cross(n0, n2) * p1.w - math.cross(n0, n1) * p2.w) * (1.0f / det);
@@ -225,7 +223,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
Size = instanceDataSize, Size = instanceDataSize,
Stride = (uint)sizeof(InstanceData), Stride = (uint)sizeof(InstanceData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Upload, // Upload directly for simplicity in testing HeapType = HeapType.Upload, // Upload directly for simplicity in testing
}; };
instanceBufferHandle = resourceManager.CreateTransientBuffer(in instanceBufferDesc, "Instance Buffer"); instanceBufferHandle = resourceManager.CreateTransientBuffer(in instanceBufferDesc, "Instance Buffer");
@@ -255,9 +253,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
}; };
} }
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest)); ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.CommandBuffer.UploadBuffer(instanceBufferHandle, instanceDataArray.AsSpan()); ctx.UploadBuffer(instanceBufferHandle, instanceDataArray.AsSpan());
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource)); ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(instanceBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
// 2. Allocate and populate View Data buffer // 2. Allocate and populate View Data buffer
var viewBufferDesc = new BufferDesc var viewBufferDesc = new BufferDesc
@@ -265,7 +263,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
Size = (uint)sizeof(ViewData), Size = (uint)sizeof(ViewData),
Stride = (uint)sizeof(ViewData), Stride = (uint)sizeof(ViewData),
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Upload, HeapType = HeapType.Upload,
}; };
viewBufferHandle = resourceManager.CreateTransientBuffer(in viewBufferDesc, "View Buffer"); viewBufferHandle = resourceManager.CreateTransientBuffer(in viewBufferDesc, "View Buffer");
@@ -282,9 +280,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
screenSize = new float4(request.view.sensorSize.x, request.view.sensorSize.y, 1.0f / request.view.sensorSize.x, 1.0f / request.view.sensorSize.y) screenSize = new float4(request.view.sensorSize.x, request.view.sensorSize.y, 1.0f / request.view.sensorSize.x, 1.0f / request.view.sensorSize.y)
}; };
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest)); ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.CommandBuffer.UploadBuffer(viewBufferHandle, new ReadOnlySpan<ViewData>(in viewData)); ctx.UploadBuffer(viewBufferHandle, new ReadOnlySpan<ViewData>(in viewData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource)); ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(viewBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
// 3. Allocate and populate Global Frame Data buffer // 3. Allocate and populate Global Frame Data buffer
var frameDataSize = (uint)sizeof(FrameData); var frameDataSize = (uint)sizeof(FrameData);
@@ -293,7 +291,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
Size = frameDataSize, Size = frameDataSize,
Stride = frameDataSize, Stride = frameDataSize,
Usage = BufferUsage.Raw | BufferUsage.ShaderResource, Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
MemoryType = ResourceMemoryType.Upload, HeapType = HeapType.Upload,
}; };
frameBufferHandle = resourceManager.CreateTransientBuffer(in frameBufferDesc, "Frame Buffer"); frameBufferHandle = resourceManager.CreateTransientBuffer(in frameBufferDesc, "Frame Buffer");
@@ -305,9 +303,9 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
instanceBufferIndex = resourceDatabase.GetBindlessIndex(instanceBufferResource), instanceBufferIndex = resourceDatabase.GetBindlessIndex(instanceBufferResource),
}; };
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, null, BarrierSync.Copy, null, BarrierAccess.CopyDest)); ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierAccess.CopyDest));
ctx.CommandBuffer.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData)); ctx.UploadBuffer(frameBufferHandle, new ReadOnlySpan<FrameData>(in frameData));
ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.Copy, BarrierSync.AllShading, BarrierAccess.CopyDest, BarrierAccess.ShaderResource)); ctx.CommandBuffer.Barrier(BarrierDesc.Buffer(frameBufferResource, BarrierSync.AllShading, BarrierAccess.ShaderResource));
if (request.renderFunc != null) if (request.renderFunc != null)
{ {
@@ -360,7 +358,7 @@ public unsafe partial class TestRenderPipeline : IRenderPipeline
builder.SetColorAttachment(backbuffer, 0); builder.SetColorAttachment(backbuffer, 0);
builder.SetDepthAttachment(depth); builder.SetDepthAttachment(depth);
builder.SetRenderFunc<MeshletDebugPassData>(static (data, ctx)=> builder.SetRenderFunc<MeshletDebugPassData>(static (data, ctx) =>
{ {
ctx.SetGlobalData(data.globalIndex, data.viewIndex); ctx.SetGlobalData(data.globalIndex, data.viewIndex);
ctx.SetInstanceData(data.instanceIndex); ctx.SetInstanceData(data.instanceIndex);

View File

@@ -10,6 +10,7 @@ using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using System.Diagnostics;
namespace Ghost.Graphics.Test.Windows; namespace Ghost.Graphics.Test.Windows;
@@ -113,9 +114,9 @@ public sealed partial class GraphicsTestWindow : Window
//MeshBuilder.CreateCube(0.75f, default, Allocator.Persistent, out var vertices, out var indices); //MeshBuilder.CreateCube(0.75f, default, Allocator.Persistent, out var vertices, out var indices);
Utilities.MeshUtility.LoadMesh("F:/c/SimpleRayTracer/native/assets/bunny.obj", Allocator.Persistent, out var vertices, out var indices).ThrowIfFailed(); Utilities.MeshUtility.LoadMesh("F:/c/SimpleRayTracer/native/assets/bunny.obj", Allocator.Persistent, out var vertices, out var indices).ThrowIfFailed();
// TODO: Put this to the beginning of the frame without createing another command buffer? // TODO: Put this to the beginning of the frame without creating another command buffer?
using var directCmd = _renderSystem.GraphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics); using var directCmd = _renderSystem.GraphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
var ctx = new RenderingContext(_renderSystem.GraphicsEngine, _renderSystem.ResourceManager, directCmd); var ctx = new RenderContext(_renderSystem.GraphicsEngine, _renderSystem.ResourceManager, directCmd);
using var cmdAllocator = _renderSystem.GraphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics); using var cmdAllocator = _renderSystem.GraphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics);
directCmd.Begin(cmdAllocator); directCmd.Begin(cmdAllocator);
@@ -158,7 +159,6 @@ public sealed partial class GraphicsTestWindow : Window
_renderSystem?.ResourceManager.ReleaseMesh(_meshHandle); _renderSystem?.ResourceManager.ReleaseMesh(_meshHandle);
_swapChain?.Dispose();
//_jobScheduler.Dispose(); //_jobScheduler.Dispose();
_renderSystem?.Dispose(); _renderSystem?.Dispose();
@@ -203,8 +203,9 @@ public sealed partial class GraphicsTestWindow : Window
return; return;
} }
if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.MaxFrameLatency) if (_renderSystem.TryAcquireCPUFrame())
{ {
//Debug.WriteLine($"CPU: Frame started.");
_world.SystemManager.UpdateAll(default); _world.SystemManager.UpdateAll(default);
_renderSystem.SignalCPUReady(); _renderSystem.SignalCPUReady();
} }