feat(engine): refactor resource mgmt & render pipeline
Refactors engine infrastructure for improved resource/service management and render pipeline extensibility. Replaces World’s resource API with a service-based API. Splits IGraphicsEngine’s RenderFrame into BeginFrame/EndFrame. Adds support for pluggable render pipelines in RenderSystem. Replaces disposed checks with Debug.Assert in performance paths. Updates RenderExtractionSystem and render loop for new APIs. Improves diagnostics and code clarity. BREAKING CHANGE: Resource API replaced with service API; render pipeline and frame lifecycle interfaces changed.
This commit is contained in:
@@ -390,6 +390,16 @@ public static class ResultExtensions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Result Then(this Result result, Func<Result> func)
|
||||||
|
{
|
||||||
|
if (result.IsFailure)
|
||||||
|
{
|
||||||
|
return Result.Failure(result.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return func();
|
||||||
|
}
|
||||||
|
|
||||||
public static Result<U> Then<T, U>(this Result<T> result, Func<T, Result<U>> func)
|
public static Result<U> Then<T, U>(this Result<T> result, Func<T, Result<U>> func)
|
||||||
{
|
{
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Engine.Components;
|
using Ghost.Engine.Components;
|
||||||
using Ghost.Entities;
|
using Ghost.Entities;
|
||||||
|
using Ghost.Graphics;
|
||||||
using Ghost.Graphics.Core;
|
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.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using Misaki.HighPerformance.Mathematics.Geometry;
|
using Misaki.HighPerformance.Mathematics.Geometry;
|
||||||
|
|
||||||
@@ -11,14 +13,14 @@ namespace Ghost.Engine.Systems;
|
|||||||
|
|
||||||
public class RenderExtractionSystem : ISystem
|
public class RenderExtractionSystem : ISystem
|
||||||
{
|
{
|
||||||
private IGraphicsEngine _graphicsEngine = null!;
|
private RenderSystem _renderSystem = null!;
|
||||||
|
|
||||||
private Identifier<EntityQuery> _cameraQueryID;
|
private Identifier<EntityQuery> _cameraQueryID;
|
||||||
private Identifier<EntityQuery> _meshQueryID;
|
private Identifier<EntityQuery> _meshQueryID;
|
||||||
|
|
||||||
public void Initialize(ref readonly SystemAPI systemAPI)
|
public void Initialize(ref readonly SystemAPI systemAPI)
|
||||||
{
|
{
|
||||||
_graphicsEngine = systemAPI.World.GetResource<IGraphicsEngine>();
|
_renderSystem = systemAPI.World.GetService<RenderSystem>();
|
||||||
|
|
||||||
var builder = new QueryBuilder();
|
var builder = new QueryBuilder();
|
||||||
|
|
||||||
@@ -77,13 +79,15 @@ public class RenderExtractionSystem : ISystem
|
|||||||
ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID);
|
ref var cameraQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_cameraQueryID);
|
||||||
ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID);
|
ref var meshQuery = ref systemAPI.World.ComponentManager.GetEntityQueryReference(_meshQueryID);
|
||||||
|
|
||||||
|
var renderRequests = new UnsafeList<RenderRequest>(cameraQuery.CalculateEntityCount(), Allocator.Temp);
|
||||||
|
|
||||||
// TODO: We should extract the render record for each camera because different cameras may have different culling results.
|
// TODO: We should extract the render record for each camera because different cameras may have different culling results.
|
||||||
foreach (var (cam, camLtw) in cameraQuery.GetComponentIterator<Camera, LocalToWorld>())
|
foreach (var (cam, camLtw) in cameraQuery.GetComponentIterator<Camera, LocalToWorld>())
|
||||||
{
|
{
|
||||||
ref readonly var camRef = ref cam.Get();
|
ref readonly var camRef = ref cam.Get();
|
||||||
ref readonly var camLtwRef = ref camLtw.Get();
|
ref readonly var camLtwRef = ref camLtw.Get();
|
||||||
|
|
||||||
var rtResult = _graphicsEngine.ResourceDatabase.GetResourceDescription(camRef.colorTarget.AsResource());
|
var rtResult = _renderSystem.GraphicsEngine.ResourceDatabase.GetResourceDescription(camRef.colorTarget.AsResource());
|
||||||
if (rtResult.IsFailure)
|
if (rtResult.IsFailure)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -206,7 +210,6 @@ public class RenderExtractionSystem : ISystem
|
|||||||
var viewPos = camLtwRef.matrix.c3.xyz;
|
var viewPos = camLtwRef.matrix.c3.xyz;
|
||||||
var frustum = CreateFrustum(camRef, vp, viewDir, viewPos);
|
var frustum = CreateFrustum(camRef, vp, viewDir, viewPos);
|
||||||
|
|
||||||
// TODO: Send this to render pipeline.
|
|
||||||
var request = new RenderRequest
|
var request = new RenderRequest
|
||||||
{
|
{
|
||||||
colorTarget = camRef.colorTarget,
|
colorTarget = camRef.colorTarget,
|
||||||
@@ -236,7 +239,11 @@ public class RenderExtractionSystem : ISystem
|
|||||||
renderingLayerMask = camRef.renderingLayerMask,
|
renderingLayerMask = camRef.renderingLayerMask,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
renderRequests.Add(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_renderPipeline.Render(new RenderContext(), renderRequests.AsSpan());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup(ref readonly SystemAPI systemAPI)
|
public void Cleanup(ref readonly SystemAPI systemAPI)
|
||||||
|
|||||||
@@ -445,8 +445,7 @@ public unsafe partial struct EntityQuery : IDisposable
|
|||||||
return new ChunkIterator(_matchingArchetypes.AsReadOnly(), world);
|
return new ChunkIterator(_matchingArchetypes.AsReadOnly(), world);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public readonly int CalculateEntityCount()
|
||||||
public readonly int GetEntityCount()
|
|
||||||
{
|
{
|
||||||
var total = 0;
|
var total = 0;
|
||||||
var world = World.GetWorld(_worldID);
|
var world = World.GetWorld(_worldID);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public abstract class SystemBase : ISystem
|
|||||||
foreach (var queryID in _requiredQueries)
|
foreach (var queryID in _requiredQueries)
|
||||||
{
|
{
|
||||||
ref var query = ref World.ComponentManager.GetEntityQueryReference(new Identifier<EntityQuery>(queryID));
|
ref var query = ref World.ComponentManager.GetEntityQueryReference(new Identifier<EntityQuery>(queryID));
|
||||||
if (query.GetEntityCount() == 0)
|
if (query.CalculateEntityCount() == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Misaki.HighPerformance.Jobs;
|
using Misaki.HighPerformance.Jobs;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using TerraFX.Interop.Windows;
|
|
||||||
|
|
||||||
namespace Ghost.Entities;
|
namespace Ghost.Entities;
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
private readonly ComponentManager _componentManager;
|
private readonly ComponentManager _componentManager;
|
||||||
private readonly SystemManager _systemManager;
|
private readonly SystemManager _systemManager;
|
||||||
|
|
||||||
private readonly Dictionary<Type, object> _globalResource;
|
private readonly Dictionary<Type, object> _services;
|
||||||
|
|
||||||
private int _version;
|
private int _version;
|
||||||
private bool _disposed = false;
|
private bool _disposed = false;
|
||||||
@@ -140,7 +139,7 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
_componentManager = new ComponentManager(this);
|
_componentManager = new ComponentManager(this);
|
||||||
_systemManager = new SystemManager(this);
|
_systemManager = new SystemManager(this);
|
||||||
|
|
||||||
_globalResource = new Dictionary<Type, object>();
|
_services = new Dictionary<Type, object>();
|
||||||
|
|
||||||
if (jobScheduler != null)
|
if (jobScheduler != null)
|
||||||
{
|
{
|
||||||
@@ -195,20 +194,20 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
/// Registers or overwrites a global resource in the world.
|
/// Registers or overwrites a global resource in the world.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetResource<T>(T resource)
|
public void AddService<T>(T resource)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
_globalResource[typeof(T)] = resource;
|
_services[typeof(T)] = resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves a global resource from the world.
|
/// Retrieves a global resource from the world.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public T GetResource<T>()
|
public T GetService<T>()
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
if (_globalResource.TryGetValue(typeof(T), out var resource))
|
if (_services.TryGetValue(typeof(T), out var resource))
|
||||||
{
|
{
|
||||||
return (T)resource;
|
return (T)resource;
|
||||||
}
|
}
|
||||||
@@ -220,10 +219,10 @@ public partial class World : IDisposable, IEquatable<World>
|
|||||||
/// Checks if a global resource exists.
|
/// Checks if a global resource exists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool HasResource<T>()
|
public bool HasService<T>()
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
return _globalResource.ContainsKey(typeof(T));
|
return _services.ContainsKey(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(World? other)
|
public bool Equals(World? other)
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ThrowIfDisposed()
|
private void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Ghost.Graphics.D3D12.Utilities;
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel;
|
using Misaki.HighPerformance.LowLevel;
|
||||||
|
using System.Diagnostics;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public void Submit(ICommandBuffer commandBuffer)
|
public void Submit(ICommandBuffer commandBuffer)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (commandBuffer.IsEmpty)
|
if (commandBuffer.IsEmpty)
|
||||||
{
|
{
|
||||||
@@ -86,7 +87,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
||||||
executableIndices.Fill(-1);
|
executableIndices.Fill(-1);
|
||||||
@@ -129,7 +130,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public ulong Signal(ulong value)
|
public ulong Signal(ulong value)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
_fenceValue = value;
|
_fenceValue = value;
|
||||||
ThrowIfFailed(_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
|
ThrowIfFailed(_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
|
||||||
@@ -138,7 +139,7 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public void WaitForValue(ulong value)
|
public void WaitForValue(ulong value)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (_fence.Get()->GetCompletedValue() < value)
|
if (_fence.Get()->GetCompletedValue() < value)
|
||||||
{
|
{
|
||||||
@@ -152,13 +153,13 @@ internal unsafe class D3D12CommandQueue : ICommandQueue
|
|||||||
|
|
||||||
public ulong GetCompletedValue()
|
public ulong GetCompletedValue()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
return _fence.Get()->GetCompletedValue();
|
return _fence.Get()->GetCompletedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WaitIdle()
|
public void WaitIdle()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
||||||
WaitForValue(fenceValue);
|
WaitForValue(fenceValue);
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
#define ENABLE_DEBUG
|
#define ENABLE_DEBUG_LAYER
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
public static class D3D12GraphicsEngineFactory
|
public static class D3D12GraphicsEngineFactory
|
||||||
{
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static IGraphicsEngine Create(GraphicsEngineDesc desc)
|
public static IGraphicsEngine Create(GraphicsEngineDesc desc)
|
||||||
{
|
{
|
||||||
return new D3D12GraphicsEngine(desc);
|
return new D3D12GraphicsEngine(desc);
|
||||||
@@ -22,7 +24,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
{
|
{
|
||||||
private readonly GraphicsEngineDesc _desc;
|
private readonly GraphicsEngineDesc _desc;
|
||||||
|
|
||||||
#if ENABLE_DEBUG
|
#if ENABLE_DEBUG_LAYER
|
||||||
private readonly D3D12DebugLayer _debugLayer;
|
private readonly D3D12DebugLayer _debugLayer;
|
||||||
#endif
|
#endif
|
||||||
private readonly D3D12RenderDevice _device;
|
private readonly D3D12RenderDevice _device;
|
||||||
@@ -46,7 +48,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
{
|
{
|
||||||
_desc = desc;
|
_desc = desc;
|
||||||
|
|
||||||
#if ENABLE_DEBUG
|
#if ENABLE_DEBUG_LAYER
|
||||||
_debugLayer = new D3D12DebugLayer();
|
_debugLayer = new D3D12DebugLayer();
|
||||||
#endif
|
#endif
|
||||||
_device = new D3D12RenderDevice();
|
_device = new D3D12RenderDevice();
|
||||||
@@ -67,6 +69,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ThrowIfDisposed()
|
private void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
@@ -118,28 +121,20 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _desc.FrameBufferCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result RenderFrame(ICommandAllocator commandAllocator, uint cpuFenceValue, uint gpuFenceValue)
|
public Result BeginFrame(uint cpuFenceValue, uint gpuFenceValue)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
var r = Result.Success();
|
|
||||||
|
|
||||||
_resourceDatabase.BeginFrame(cpuFenceValue);
|
_resourceDatabase.BeginFrame(cpuFenceValue);
|
||||||
|
return Result.Success();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: We should not handle renderers in graphics engine since the purpose of graphics engine is to provide low-level graphics resource management and command buffer creation.
|
public Result EndFrame(uint cpuFenceValue, uint gpuFenceValue)
|
||||||
// We need to migrate this to IRenderPipeline instead when it's ready.
|
|
||||||
foreach (var renderer in _renderers)
|
|
||||||
{
|
{
|
||||||
r = renderer.Render(commandAllocator);
|
ThrowIfDisposed();
|
||||||
if (r.IsFailure)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_resourceDatabase.EndFrame(gpuFenceValue);
|
_resourceDatabase.EndFrame(gpuFenceValue);
|
||||||
|
return Result.Success();
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -163,7 +158,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
_descriptorAllocator.Dispose();
|
_descriptorAllocator.Dispose();
|
||||||
_shaderCompiler.Dispose();
|
_shaderCompiler.Dispose();
|
||||||
_device.Dispose();
|
_device.Dispose();
|
||||||
#if ENABLE_DEBUG
|
#if ENABLE_DEBUG_LAYER
|
||||||
_debugLayer.Dispose();
|
_debugLayer.Dispose();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
|||||||
using Ghost.Graphics.D3D12.Utilities;
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel;
|
using Misaki.HighPerformance.LowLevel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
@@ -601,7 +602,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default)
|
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
CheckTexture2DSize(desc.Width, desc.Height);
|
CheckTexture2DSize(desc.Width, desc.Height);
|
||||||
|
|
||||||
@@ -699,7 +700,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default)
|
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
var textureDesc = desc.ToTextureDescription();
|
var textureDesc = desc.ToTextureDescription();
|
||||||
return CreateTexture(in textureDesc, name, options);
|
return CreateTexture(in textureDesc, name, options);
|
||||||
@@ -707,7 +708,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default)
|
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
CheckBufferSize(desc.Size);
|
CheckBufferSize(desc.Size);
|
||||||
|
|
||||||
var resourceDesc = desc.ToD3D12ResourceDesc();
|
var resourceDesc = desc.ToD3D12ResourceDesc();
|
||||||
@@ -839,7 +840,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
|||||||
|
|
||||||
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc)
|
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (_resourceDatabase.TryGetSampler(in desc, out var id))
|
if (_resourceDatabase.TryGetSampler(in desc, out var id))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Ghost.Graphics.RHI;
|
|||||||
using Misaki.HighPerformance.LowLevel;
|
using Misaki.HighPerformance.LowLevel;
|
||||||
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.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null)
|
internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (pResource == null)
|
if (pResource == null)
|
||||||
{
|
{
|
||||||
@@ -156,7 +157,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
public unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
if (allocation == null)
|
if (allocation == null)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
@@ -186,13 +187,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public bool HasResource(Handle<GPUResource> handle)
|
public bool HasResource(Handle<GPUResource> handle)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
return _resources.Contains(handle.ID, handle.Generation);
|
return _resources.Contains(handle.ID, handle.Generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RefResult<ResourceRecord, Error> GetResourceRecord(Handle<GPUResource> handle)
|
public RefResult<ResourceRecord, Error> GetResourceRecord(Handle<GPUResource> handle)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||||
if (!exist)
|
if (!exist)
|
||||||
@@ -267,7 +268,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public string? GetResourceName(Handle<GPUResource> handle)
|
public string? GetResourceName(Handle<GPUResource> handle)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
#if DEBUG || GHOST_EDITOR
|
#if DEBUG || GHOST_EDITOR
|
||||||
if (_resourceName.TryGetValue(handle, out var name))
|
if (_resourceName.TryGetValue(handle, out var name))
|
||||||
@@ -280,7 +281,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public void ReleaseResource(Handle<GPUResource> handle)
|
public void ReleaseResource(Handle<GPUResource> handle)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (_resources.TryGetElementAt(handle.ID, handle.Generation, out var record))
|
if (_resources.TryGetElementAt(handle.ID, handle.Generation, out var record))
|
||||||
{
|
{
|
||||||
@@ -299,7 +300,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public void ReleaseResourceImmediately(Handle<GPUResource> handle)
|
public void ReleaseResourceImmediately(Handle<GPUResource> handle)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||||
if (!exist || !info.Allocated)
|
if (!exist || !info.Allocated)
|
||||||
@@ -313,7 +314,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public Identifier<Sampler> AddSampler(ref readonly SamplerDesc desc, int id)
|
public Identifier<Sampler> AddSampler(ref readonly SamplerDesc desc, int id)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (_samplers.ContainsKey(desc))
|
if (_samplers.ContainsKey(desc))
|
||||||
{
|
{
|
||||||
@@ -328,13 +329,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id)
|
public bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
return _samplers.TryGetValue(desc, out id);
|
return _samplers.TryGetValue(desc, out id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReleaseSampler(Identifier<Sampler> id)
|
public void ReleaseSampler(Identifier<Sampler> id)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
// NOTE: We almost never release samplers individually, because they are cheap and can be reused.
|
// 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.
|
// Ideally we would release all samplers at once when disposing the ResourceDatabase.
|
||||||
@@ -360,13 +361,13 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
public void BeginFrame(uint currentFrameFenceValue)
|
public void BeginFrame(uint currentFrameFenceValue)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
_currentFrameFenceValue = currentFrameFenceValue;
|
_currentFrameFenceValue = currentFrameFenceValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void EndFrame(uint completedFenceValue)
|
public void EndFrame(uint completedFenceValue)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
while (_releaseQueue.Count > 0)
|
while (_releaseQueue.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -384,7 +385,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
|
|
||||||
internal void ReleaseAllResourcesImmediately()
|
internal void ReleaseAllResourcesImmediately()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
foreach (ref var record in _resources)
|
foreach (ref var record in _resources)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,8 +68,10 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
SetScale(desc.ScaleX, desc.ScaleY);
|
SetScale(desc.ScaleX, desc.ScaleY);
|
||||||
|
|
||||||
if (desc.Target.Type == SwapChainTargetType.Composition)
|
if (desc.Target.Type == SwapChainTargetType.Composition)
|
||||||
|
{
|
||||||
_compositionSurface = desc.Target.CompositionSurface;
|
_compositionSurface = desc.Target.CompositionSurface;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
~D3D12SwapChain()
|
~D3D12SwapChain()
|
||||||
{
|
{
|
||||||
@@ -169,20 +171,20 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public Handle<Texture> GetCurrentBackBuffer()
|
public Handle<Texture> GetCurrentBackBuffer()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public ReadOnlySpan<Handle<Texture>> GetBackBuffers()
|
public ReadOnlySpan<Handle<Texture>> GetBackBuffers()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
return _backBuffers.AsSpan();
|
return _backBuffers.AsSpan();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Present(bool vsync = true)
|
public void Present(bool vsync = true)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
var presentFlags = 0u;
|
var presentFlags = 0u;
|
||||||
var syncInterval = vsync ? 1u : 0u;
|
var syncInterval = vsync ? 1u : 0u;
|
||||||
@@ -192,7 +194,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
|
|
||||||
public void Resize(uint width, uint height)
|
public void Resize(uint width, uint height)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
if (Width == width && Height == height)
|
if (Width == width && Height == height)
|
||||||
{
|
{
|
||||||
@@ -215,6 +217,8 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
|
|
||||||
public void SetScale(float scaleX, float scaleY)
|
public void SetScale(float scaleX, float scaleY)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(!_disposed);
|
||||||
|
|
||||||
var inverseScaleX = 1.0f / scaleX;
|
var inverseScaleX = 1.0f / scaleX;
|
||||||
var inverseScaleY = 1.0f / scaleY;
|
var inverseScaleY = 1.0f / scaleY;
|
||||||
|
|
||||||
|
|||||||
@@ -78,11 +78,18 @@ public interface IGraphicsEngine : IDisposable
|
|||||||
ISwapChain CreateSwapChain(SwapChainDesc desc);
|
ISwapChain CreateSwapChain(SwapChainDesc desc);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renders the current frame.
|
/// Begin the current frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
|
||||||
/// <param name="cpuFenceValue">CPU fence value for synchronization</param>
|
/// <param name="cpuFenceValue">CPU fence value for synchronization</param>
|
||||||
/// <param name="gpuFenceValue">GPU fence value for synchronization</param>
|
/// <param name="gpuFenceValue">GPU fence value for synchronization</param>
|
||||||
/// <returns>Result of the rendering operation</returns>
|
/// <returns>Result of the begin frame operation</returns>
|
||||||
Result RenderFrame(ICommandAllocator commandAllocator, uint cpuFenceValue, uint gpuFenceValue);
|
Result BeginFrame(uint cpuFenceValue, uint gpuFenceValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// End the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cpuFenceValue">CPU fence value for synchronization</param>
|
||||||
|
/// <param name="gpuFenceValue">GPU fence value for synchronization</param>
|
||||||
|
/// <returns>Result of the end frame operation</returns>
|
||||||
|
Result EndFrame(uint cpuFenceValue, uint gpuFenceValue);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderGraphModule;
|
namespace Ghost.Graphics.RenderGraphModule;
|
||||||
|
|
||||||
@@ -175,6 +176,8 @@ internal class RenderGraphBuilder : IRasterRenderGraphBuilder, IComputeRenderGra
|
|||||||
_disposed = false;
|
_disposed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ThrowIfDisposed()
|
private void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RenderGraphModule;
|
using Ghost.Graphics.RenderGraphModule;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RenderPipeline;
|
namespace Ghost.Graphics.RenderPipeline;
|
||||||
|
|
||||||
public sealed class GhostRenderPipelineSettings : IRenderPipelineSettings
|
public sealed class GhostRenderPipelineSettings : IRenderPipelineSettings
|
||||||
{
|
{
|
||||||
public static IRenderPipeline CreatePipeline(RenderSystem renderSystem)
|
public IRenderPipeline CreatePipeline(RenderSystem renderSystem)
|
||||||
{
|
{
|
||||||
return new GhostRenderPipeline(renderSystem);
|
return new GhostRenderPipeline(renderSystem);
|
||||||
}
|
}
|
||||||
@@ -24,6 +25,7 @@ public unsafe partial class GhostRenderPipeline : IRenderPipeline
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
protected void ThrowIfDisposed()
|
protected void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Ghost.Graphics.RenderPipeline;
|
|||||||
|
|
||||||
public interface IRenderPipelineSettings
|
public interface IRenderPipelineSettings
|
||||||
{
|
{
|
||||||
static abstract IRenderPipeline CreatePipeline(RenderSystem renderSystem);
|
IRenderPipeline CreatePipeline(RenderSystem renderSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IRenderPipeline : IDisposable
|
public interface IRenderPipeline : IDisposable
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.D3D12;
|
using Ghost.Graphics.D3D12;
|
||||||
|
using Ghost.Graphics.RenderPipeline;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ghost.Graphics;
|
namespace Ghost.Graphics;
|
||||||
|
|
||||||
@@ -61,6 +63,7 @@ public class RenderSystem : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly RenderSystemDesc _config;
|
private readonly RenderSystemDesc _config;
|
||||||
|
|
||||||
private readonly IGraphicsEngine _graphicsEngine;
|
private readonly IGraphicsEngine _graphicsEngine;
|
||||||
private readonly ResourceManager _resourceManager;
|
private readonly ResourceManager _resourceManager;
|
||||||
|
|
||||||
@@ -69,6 +72,9 @@ public class RenderSystem : IDisposable
|
|||||||
private readonly AutoResetEvent _shutdownEvent;
|
private readonly AutoResetEvent _shutdownEvent;
|
||||||
private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest;
|
private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest;
|
||||||
|
|
||||||
|
private IRenderPipelineSettings _renderPipelineSettings;
|
||||||
|
private IRenderPipeline _renderPipeline;
|
||||||
|
|
||||||
private uint _frameIndex;
|
private uint _frameIndex;
|
||||||
private uint _cpuFenceValue;
|
private uint _cpuFenceValue;
|
||||||
private uint _gpuFenceValue;
|
private uint _gpuFenceValue;
|
||||||
@@ -85,6 +91,24 @@ public class RenderSystem : IDisposable
|
|||||||
public uint FrameIndex => _frameIndex;
|
public uint FrameIndex => _frameIndex;
|
||||||
public uint MaxFrameLatency => _config.FrameBufferCount;
|
public uint MaxFrameLatency => _config.FrameBufferCount;
|
||||||
|
|
||||||
|
public IRenderPipelineSettings RenderPipelineSettings
|
||||||
|
{
|
||||||
|
get => _renderPipelineSettings;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Debug.Assert(value != null, "RenderPipelineSettings cannot be set to null.");
|
||||||
|
Debug.Assert(!_disposed, "Cannot set RenderPipelineSettings on a disposed RenderSystem.");
|
||||||
|
|
||||||
|
if (value == _renderPipelineSettings)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderPipelineSettings = value;
|
||||||
|
_renderPipeline = _renderPipelineSettings.CreatePipeline(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal RenderSystem(RenderSystemDesc desc)
|
internal RenderSystem(RenderSystemDesc desc)
|
||||||
{
|
{
|
||||||
_config = desc;
|
_config = desc;
|
||||||
@@ -137,6 +161,9 @@ public class RenderSystem : IDisposable
|
|||||||
_shutdownEvent = new AutoResetEvent(false);
|
_shutdownEvent = new AutoResetEvent(false);
|
||||||
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
|
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
|
||||||
|
|
||||||
|
_renderPipelineSettings = new GhostRenderPipelineSettings();
|
||||||
|
_renderPipeline = _renderPipelineSettings.CreatePipeline(this);
|
||||||
|
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
}
|
}
|
||||||
@@ -148,6 +175,18 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
private void RenderLoop()
|
private void RenderLoop()
|
||||||
{
|
{
|
||||||
|
void StopRenderLoop(Result result)
|
||||||
|
{
|
||||||
|
Debug.Assert(result.IsFailure, "StopRenderLoop should only be called with a failure result.");
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
_shutdownEvent.Set();
|
||||||
|
#if DEBUG
|
||||||
|
Debugger.Break();
|
||||||
|
#endif
|
||||||
|
Logger.LogError($"Render failed: {result.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
var waitHandles = new WaitHandle[] { null!, _shutdownEvent };
|
var waitHandles = new WaitHandle[] { null!, _shutdownEvent };
|
||||||
|
|
||||||
while (_isRunning)
|
while (_isRunning)
|
||||||
@@ -166,13 +205,13 @@ public class RenderSystem : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only proceed if CPU ready event was signaled
|
// Only proceed if CPU ready event was signaled
|
||||||
if (waitResult == 0)
|
if (waitResult != 0)
|
||||||
{
|
{
|
||||||
if (frameResource.FenceValue > 0)
|
continue;
|
||||||
{
|
|
||||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
|
||||||
|
|
||||||
if (!_resizeRequest.IsEmpty)
|
if (!_resizeRequest.IsEmpty)
|
||||||
{
|
{
|
||||||
//WaitIdle();
|
//WaitIdle();
|
||||||
@@ -204,14 +243,24 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
frameResource.CommandAllocator.Reset();
|
frameResource.CommandAllocator.Reset();
|
||||||
|
|
||||||
var r = _graphicsEngine.RenderFrame(frameResource.CommandAllocator, _cpuFenceValue, _gpuFenceValue);
|
var r = _graphicsEngine.BeginFrame(_cpuFenceValue, _gpuFenceValue);
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
{
|
{
|
||||||
_isRunning = false;
|
StopRenderLoop(r);
|
||||||
#if DEBUG
|
}
|
||||||
System.Diagnostics.Debugger.Break();
|
|
||||||
#endif
|
var cmd = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
|
||||||
Logger.LogError($"RenderFrame failed: {r.Message}");
|
var renderCtx = new RenderContext
|
||||||
|
{
|
||||||
|
CommandBuffer = cmd,
|
||||||
|
};
|
||||||
|
|
||||||
|
_renderPipeline.Render(renderCtx, default);
|
||||||
|
|
||||||
|
r = _graphicsEngine.EndFrame(_cpuFenceValue, _gpuFenceValue);
|
||||||
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
StopRenderLoop(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
_gpuFenceValue++;
|
_gpuFenceValue++;
|
||||||
@@ -220,11 +269,10 @@ public class RenderSystem : IDisposable
|
|||||||
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
|
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal void Start()
|
internal void Start()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed, "Cannot start a disposed RenderSystem.");
|
||||||
|
|
||||||
if (_isRunning)
|
if (_isRunning)
|
||||||
{
|
{
|
||||||
@@ -237,7 +285,7 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
internal void Stop()
|
internal void Stop()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed, "Cannot stop a disposed RenderSystem.");
|
||||||
|
|
||||||
if (!_isRunning)
|
if (!_isRunning)
|
||||||
{
|
{
|
||||||
@@ -251,7 +299,7 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
internal void SignalCPUReady()
|
internal void SignalCPUReady()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed, "Cannot signal CPU ready on a disposed RenderSystem.");
|
||||||
|
|
||||||
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||||
_frameResources[eventIndex].CpuReadyEvent.Set();
|
_frameResources[eventIndex].CpuReadyEvent.Set();
|
||||||
@@ -260,13 +308,13 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
internal void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize)
|
internal void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed, "Cannot request swap chain resize on a disposed RenderSystem.");
|
||||||
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
|
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool WaitForGPUReady(int timeOut = -1)
|
public bool WaitForGPUReady(int timeOut = -1)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
Debug.Assert(!_disposed, "Cannot wait for GPU ready on a disposed RenderSystem.");
|
||||||
|
|
||||||
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
var eventIndex = (int)(_cpuFenceValue % _config.FrameBufferCount);
|
||||||
return _frameResources[eventIndex].GpuReadyEvent.WaitOne(timeOut);
|
return _frameResources[eventIndex].GpuReadyEvent.WaitOne(timeOut);
|
||||||
@@ -274,6 +322,7 @@ public class RenderSystem : IDisposable
|
|||||||
|
|
||||||
public void WaitIdle()
|
public void WaitIdle()
|
||||||
{
|
{
|
||||||
|
Debug.Assert(!_disposed, "Cannot wait idle on a disposed RenderSystem.");
|
||||||
foreach (var frameResource in _frameResources)
|
foreach (var frameResource in _frameResources)
|
||||||
{
|
{
|
||||||
if (frameResource.FenceValue > 0)
|
if (frameResource.FenceValue > 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user