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:
@@ -117,7 +117,7 @@ public struct Material : IResourceReleasable
|
||||
{
|
||||
Size = shader.CBufferSize,
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var buffer = resourceAllocator.CreateBuffer(ref desc, "MaterialCBuffer");
|
||||
@@ -243,7 +243,7 @@ public struct Material : IResourceReleasable
|
||||
return _keywordMask.IsKeywordEnabled(localIndex);
|
||||
}
|
||||
|
||||
public readonly void UploadData(ICommandBuffer cmd, IResourceDatabase resourceDatabase)
|
||||
public readonly void UploadData(RenderContext ctx)
|
||||
{
|
||||
if (!_isDirty)
|
||||
{
|
||||
@@ -251,31 +251,20 @@ public struct Material : IResourceReleasable
|
||||
}
|
||||
|
||||
var cbufferResource = _cBufferCache.GpuResource.AsResource();
|
||||
var r = resourceDatabase.GetResourceBarrierData(cbufferResource);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var barrierData = r.Value;
|
||||
var desc = BarrierDesc.Buffer(
|
||||
cbufferResource,
|
||||
barrierData.sync,
|
||||
BarrierSync.Copy,
|
||||
barrierData.access,
|
||||
BarrierAccess.CopyDest);
|
||||
|
||||
cmd.Barrier(desc);
|
||||
cmd.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan());
|
||||
ctx.CommandBuffer.Barrier(desc);
|
||||
ctx.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan());
|
||||
|
||||
desc = BarrierDesc.Buffer(
|
||||
cbufferResource,
|
||||
BarrierSync.Copy,
|
||||
BarrierSync.AllShading,
|
||||
BarrierAccess.CopyDest,
|
||||
BarrierAccess.ShaderResource);
|
||||
|
||||
cmd.Barrier(desc);
|
||||
ctx.CommandBuffer.Barrier(desc);
|
||||
}
|
||||
|
||||
public void ReleaseResource(IResourceDatabase database)
|
||||
|
||||
@@ -7,13 +7,13 @@ using System.Diagnostics;
|
||||
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.
|
||||
public readonly unsafe ref struct RenderingContext
|
||||
public readonly unsafe ref struct RenderContext
|
||||
{
|
||||
private readonly IGraphicsEngine _engine;
|
||||
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 ResourceManager ResourceManager => _resourceManager;
|
||||
@@ -21,69 +21,29 @@ public readonly unsafe ref struct RenderingContext
|
||||
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
||||
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
||||
|
||||
internal RenderingContext(IGraphicsEngine engine, ResourceManager resourceManager, ICommandBuffer directCmd)
|
||||
internal RenderContext(IGraphicsEngine engine, ResourceManager resourceManager, ICommandBuffer cmd)
|
||||
{
|
||||
_engine = engine;
|
||||
_resourceManager = resourceManager;
|
||||
_directCmd = directCmd;
|
||||
}
|
||||
|
||||
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();
|
||||
_cmd = cmd;
|
||||
}
|
||||
|
||||
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;
|
||||
if (isTexture)
|
||||
{
|
||||
desc = BarrierDesc.Texture(
|
||||
resource,
|
||||
data.sync, newSync,
|
||||
data.access, newAccess,
|
||||
data.layout, newLayout);
|
||||
desc = BarrierDesc.Texture(resource, newSync, newAccess, newLayout);
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = BarrierDesc.Buffer(
|
||||
resource,
|
||||
data.sync, newSync,
|
||||
data.access, newAccess);
|
||||
desc = BarrierDesc.Buffer(resource, newSync, newAccess);
|
||||
}
|
||||
|
||||
_directCmd.Barrier(new ReadOnlySpan<BarrierDesc>(in desc));
|
||||
ResourceDatabase.SetResourceBarrierData(resource, new ResourceBarrierData(newLayout, newAccess, newSync));
|
||||
_cmd.Barrier(desc);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
var r = _engine.ResourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||
@@ -95,34 +55,43 @@ public readonly unsafe ref struct RenderingContext
|
||||
Debug.Assert(r.Value.Type == ResourceType.Buffer);
|
||||
|
||||
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)
|
||||
{
|
||||
ResourceDatabase.Map(buffer.AsResource(), 0, null, null, pData, sizeInBytes);
|
||||
ResourceDatabase.MapResource(buffer.AsResource(), 0, null, null, pData, sizeInBytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
|
||||
//var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
|
||||
var uploadDesc = new BufferDesc
|
||||
{
|
||||
Size = sizeInBytes,
|
||||
Usage = BufferUsage.Upload,
|
||||
MemoryType = ResourceMemoryType.Upload,
|
||||
HeapType = HeapType.Upload,
|
||||
};
|
||||
|
||||
var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc);
|
||||
|
||||
fixed (T* pData = data)
|
||||
if (uploadHandle.IsInvalid)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO: Make one memory pool for upload.
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the mesh data to the GPU.
|
||||
/// </summary>
|
||||
@@ -221,14 +188,14 @@ public readonly unsafe ref struct RenderingContext
|
||||
Size = (uint)(meshletData.meshlets.Count * sizeof(Meshlet)),
|
||||
Stride = (uint)sizeof(Meshlet),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
var verticesDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(meshletData.meshletVertices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
// Ensure size is multiple of 4 for Raw buffer
|
||||
var trianglesSize = (uint)meshletData.meshletTriangles.Count * sizeof(uint);
|
||||
@@ -237,7 +204,7 @@ public readonly unsafe ref struct RenderingContext
|
||||
Size = trianglesSize,
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
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)
|
||||
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();
|
||||
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,
|
||||
Usage = BufferUsage.Upload,
|
||||
MemoryType = ResourceMemoryType.Upload,
|
||||
HeapType = HeapType.Upload,
|
||||
};
|
||||
|
||||
var uploadHandle = _resourceManager.CreateTransientBuffer(in uploadDesc);
|
||||
@@ -319,18 +280,25 @@ public readonly unsafe ref struct RenderingContext
|
||||
throw new OutOfMemoryException("Failed to create upload buffer for texture data.");
|
||||
}
|
||||
|
||||
TransitionBarrier(texture.AsResource(), true, BarrierLayout.CopyDest, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||
|
||||
fixed (T* pData = data)
|
||||
try
|
||||
{
|
||||
var subresourceData = new SubResourceData
|
||||
{
|
||||
pData = pData,
|
||||
rowPitch = rowPitch,
|
||||
slicePitch = slicePitch
|
||||
};
|
||||
TransitionBarrier(texture.AsResource(), true, BarrierLayout.CopyDest, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||
|
||||
_directCmd.UpdateSubResources(texture.AsResource(), uploadHandle.AsResource(), subresourceData);
|
||||
fixed (T* pData = data)
|
||||
{
|
||||
var subresourceData = new SubResourceData
|
||||
{
|
||||
pData = pData,
|
||||
rowPitch = rowPitch,
|
||||
slicePitch = slicePitch
|
||||
};
|
||||
|
||||
_cmd.UpdateSubResources(texture.AsResource(), uploadHandle.AsResource(), subresourceData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResourceDatabase.ReleaseResource(uploadHandle.AsResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -217,6 +217,7 @@ internal sealed class RenderGraphExecutor
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
var compiledBarrier = compiledBarriers[barrierIndex++];
|
||||
@@ -231,60 +232,23 @@ internal sealed class RenderGraphExecutor
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Skip if already in target state (optimization)
|
||||
if (!compiledBarrier.AliasingPredecessor.IsValid &&
|
||||
layoutBefore == target.layout &&
|
||||
accessBefore == target.access &&
|
||||
syncBefore == target.sync)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create barrier descriptor
|
||||
BarrierDesc desc;
|
||||
if (compiledBarrier.ResourceType == RenderGraphResourceType.Texture)
|
||||
{
|
||||
desc = BarrierDesc.Texture(resourceHandle,
|
||||
syncBefore, target.sync,
|
||||
accessBefore, target.access,
|
||||
layoutBefore, target.layout,
|
||||
desc = BarrierDesc.Texture(resourceHandle, target.sync, target.access, target.layout,
|
||||
discard: compiledBarrier.Flags.HasFlag(BarrierFlags.Discard));
|
||||
}
|
||||
else
|
||||
{
|
||||
desc = BarrierDesc.Buffer(resourceHandle,
|
||||
syncBefore, target.sync,
|
||||
accessBefore, target.access);
|
||||
desc = BarrierDesc.Buffer(resourceHandle, target.sync, target.access);
|
||||
}
|
||||
|
||||
if (compiledBarrier.AliasingPredecessor.IsValid)
|
||||
{
|
||||
desc.IsAliasing = true;
|
||||
}
|
||||
|
||||
if (barrierCount >= MaxBatch)
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Diagnostics;
|
||||
|
||||
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
|
||||
{
|
||||
IRenderPipeline CreatePipeline(RenderSystem renderSystem);
|
||||
|
||||
@@ -100,7 +100,8 @@ public class RenderSystem : IDisposable
|
||||
|
||||
private uint _frameIndex;
|
||||
private ulong _cpuFenceValue;
|
||||
private ulong _gpuFenceValue;
|
||||
private ulong _submittedFenceValue;
|
||||
private ulong _completedFenceValue;
|
||||
|
||||
private bool _isRunning;
|
||||
private bool _disposed;
|
||||
@@ -112,7 +113,8 @@ public class RenderSystem : IDisposable
|
||||
public bool IsRunning => _isRunning;
|
||||
|
||||
public ulong CPUFenceValue => _cpuFenceValue;
|
||||
public ulong GPUFenceValue => _gpuFenceValue;
|
||||
public ulong SubmittedFenceValue => _submittedFenceValue;
|
||||
public ulong CompletedFenceValue => _completedFenceValue;
|
||||
public uint FrameIndex => _frameIndex;
|
||||
public uint MaxFrameLatency => _config.FrameBufferCount;
|
||||
|
||||
@@ -219,7 +221,7 @@ public class RenderSystem : IDisposable
|
||||
|
||||
while (_isRunning)
|
||||
{
|
||||
_frameIndex = (uint)(_gpuFenceValue % _config.FrameBufferCount);
|
||||
_frameIndex = (uint)(_submittedFenceValue % _config.FrameBufferCount);
|
||||
ref var frameResource = ref _frameResources[_frameIndex];
|
||||
|
||||
// Wait for either CPU ready signal or shutdown signal
|
||||
@@ -242,17 +244,7 @@ public class RenderSystem : IDisposable
|
||||
|
||||
if (!_resizeRequest.IsEmpty)
|
||||
{
|
||||
_gpuFenceValue++;
|
||||
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();
|
||||
}
|
||||
WaitIdle();
|
||||
|
||||
var keys = _resizeRequest.Keys.ToArray();
|
||||
foreach (var swapChain in keys)
|
||||
@@ -262,17 +254,19 @@ public class RenderSystem : IDisposable
|
||||
swapChain.Resize(newSize.x, newSize.y);
|
||||
}
|
||||
}
|
||||
|
||||
frameResource.GpuReadyEvent.Set();
|
||||
|
||||
continue; // Skip rendering this frame since we just resized and may have invalid render targets
|
||||
}
|
||||
|
||||
_completedFenceValue = _graphicsEngine.Device.GraphicsQueue.GetCompletedValue();
|
||||
if (_submittedFenceValue < _completedFenceValue)
|
||||
{
|
||||
_submittedFenceValue = _completedFenceValue;
|
||||
}
|
||||
|
||||
// Begin rendering for this frame
|
||||
frameResource.CommandAllocator.Reset();
|
||||
|
||||
_resourceManager.BeginFrame(_cpuFenceValue);
|
||||
var r = _graphicsEngine.BeginFrame(_cpuFenceValue);
|
||||
_resourceManager.BeginFrame(_submittedFenceValue + 1);
|
||||
var r = _graphicsEngine.BeginFrame(_submittedFenceValue + 1);
|
||||
|
||||
if (r.IsFailure)
|
||||
{
|
||||
@@ -290,11 +284,9 @@ public class RenderSystem : IDisposable
|
||||
{
|
||||
cmd.Begin(frameResource.CommandAllocator);
|
||||
|
||||
var renderCtx = new RenderContext
|
||||
{
|
||||
CommandBuffer = cmd
|
||||
};
|
||||
var renderCtx = new RenderContext(_graphicsEngine, _resourceManager, cmd);
|
||||
|
||||
//Debug.WriteLine($"GPU: Frame started.");
|
||||
_renderPipeline.Render(renderCtx, renderRequests.AsSpan());
|
||||
_swapChainManager.TransitionToPresent(cmd);
|
||||
|
||||
@@ -321,9 +313,9 @@ public class RenderSystem : IDisposable
|
||||
renderRequests.Clear();
|
||||
}
|
||||
|
||||
// End the frame and present
|
||||
_resourceManager.EndFrame(_gpuFenceValue);
|
||||
r = _graphicsEngine.EndFrame(_gpuFenceValue);
|
||||
// End the frame and retire resources based on actual GPU progress.
|
||||
_resourceManager.EndFrame(_completedFenceValue);
|
||||
r = _graphicsEngine.EndFrame(_completedFenceValue);
|
||||
|
||||
if (r.IsFailure)
|
||||
{
|
||||
@@ -331,11 +323,9 @@ public class RenderSystem : IDisposable
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepare for the next frame.
|
||||
_gpuFenceValue++;
|
||||
|
||||
_submittedFenceValue++;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Debug.Assert(!_disposed, "Cannot add render request to a disposed RenderSystem.");
|
||||
|
||||
@@ -10,6 +10,7 @@ public partial class ResourceManager
|
||||
{
|
||||
private const ulong _DEFAULT_TRANSIENT_PAGE_SIZE = 16 * 1024 * 1024; // 16MB
|
||||
|
||||
[DebuggerDisplay("Heap: {heap}, Offset: {offset}, HeapType: {heapType}, HeapFlags: {heapFlags}")]
|
||||
private struct Page
|
||||
{
|
||||
public Handle<GPUResource> heap;
|
||||
@@ -19,6 +20,7 @@ public partial class ResourceManager
|
||||
public HeapType heapType;
|
||||
}
|
||||
|
||||
[DebuggerDisplay("Page Heap: {page.heap}, RetireFrame: {retireFrame}")]
|
||||
private struct RetiringPage
|
||||
{
|
||||
public Page page;
|
||||
@@ -26,14 +28,14 @@ public partial class ResourceManager
|
||||
}
|
||||
|
||||
private UnsafeList<Page> _activePages;
|
||||
private UnsafeQueue<RetiringPage> _retiringPages;
|
||||
private Queue<RetiringPage> _retiringPages;
|
||||
|
||||
private UnsafeList<Handle<GPUResource>> _oversizedTransientResources;
|
||||
|
||||
private void InitializePool()
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
@@ -152,10 +154,10 @@ public partial class ResourceManager
|
||||
return bufHandle;
|
||||
}
|
||||
|
||||
var requiredHeapType = desc.MemoryType switch
|
||||
var requiredHeapType = desc.HeapType switch
|
||||
{
|
||||
ResourceMemoryType.Upload => HeapType.Upload,
|
||||
ResourceMemoryType.Readback => HeapType.Readback,
|
||||
HeapType.Upload => HeapType.Upload,
|
||||
HeapType.Readback => HeapType.Readback,
|
||||
_ => HeapType.Default
|
||||
};
|
||||
|
||||
@@ -265,7 +267,7 @@ public partial class ResourceManager
|
||||
}
|
||||
|
||||
_activePages.Dispose();
|
||||
_retiringPages.Dispose();
|
||||
//_retiringPages.Dispose();
|
||||
_oversizedTransientResources.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public sealed partial class ResourceManager : IDisposable
|
||||
Size = (uint)(vertices.Count * sizeof(Vertex)),
|
||||
Stride = (uint)sizeof(Vertex),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var indexBufferDesc = new BufferDesc
|
||||
@@ -91,7 +91,7 @@ public sealed partial class ResourceManager : IDisposable
|
||||
Size = (uint)(indices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var objectBufferDesc = new BufferDesc
|
||||
@@ -99,7 +99,7 @@ public sealed partial class ResourceManager : IDisposable
|
||||
Size = (uint)sizeof(MeshData),
|
||||
Stride = (uint)sizeof(MeshData),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer");
|
||||
|
||||
@@ -127,9 +127,9 @@ internal class SwapChainManager : IDisposable
|
||||
}
|
||||
|
||||
commandBuffer.Barrier(BarrierDesc.Texture(record.SwapChain.GetCurrentBackBuffer().AsResource(),
|
||||
null, BarrierSync.None,
|
||||
null, BarrierAccess.NoAccess,
|
||||
null, BarrierLayout.Present));
|
||||
BarrierSync.None,
|
||||
BarrierAccess.NoAccess,
|
||||
BarrierLayout.Present));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user