Refactor: add command allocator & render target strategies
Major refactor of graphics infrastructure: - Introduce ICommandAllocator and D3D12CommandAllocator for explicit command buffer management. - Change ICommandBuffer.Begin to require an allocator. - Add IRenderTargetStrategy abstraction with swap chain and texture implementations. - Update IRenderer to use RenderTargetStrategy instead of direct handle. - Add DPI scaling support to swap chains (ScaleX/ScaleY, SetScale). - RenderSystem now supports thread-safe swap chain resize requests. - Remove persistent copy command buffer; use per-frame allocators. - Make Logger public/static and clean up API visibility. - Update .editorconfig and debug layer enablement. These changes improve modularity, DPI-awareness, and future extensibility.
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
csharp_new_line_before_open_brace = all
|
csharp_new_line_before_open_brace = all
|
||||||
csharp_preserve_single_line_statements = true
|
csharp_preserve_single_line_statements = true
|
||||||
csharp_preserve_single_line_blocks = true
|
csharp_preserve_single_line_blocks = true
|
||||||
|
csharp_style_prefer_primary_constructors = false
|
||||||
dotnet_sort_system_directives_first = false
|
dotnet_sort_system_directives_first = false
|
||||||
dotnet_separate_import_directive_groups = false
|
dotnet_separate_import_directive_groups = false
|
||||||
max_line_length = 400
|
max_line_length = 400
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Ghost.Core;
|
namespace Ghost.Core;
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ public enum LogLevel
|
|||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
|
|
||||||
internal readonly struct LogMessage
|
public readonly struct LogMessage
|
||||||
{
|
{
|
||||||
public LogLevel Level
|
public LogLevel Level
|
||||||
{
|
{
|
||||||
@@ -51,67 +50,68 @@ internal readonly struct LogMessage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface ILogger
|
public interface ILogger
|
||||||
{
|
{
|
||||||
public ReadOnlyObservableCollection<LogMessage> Logs
|
ReadOnlyObservableCollection<LogMessage> Logs
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(string message, LogLevel level);
|
void Log(string message, LogLevel level);
|
||||||
public void Log(Exception exception);
|
void Log(Exception exception);
|
||||||
public void Assert(bool condition, string message);
|
void Assert(bool condition, string message);
|
||||||
public void Clear();
|
void Clear();
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add file logging.
|
|
||||||
internal class LoggerImplementation : ILogger
|
|
||||||
{
|
|
||||||
private readonly ObservableCollection<LogMessage> _logs = new();
|
|
||||||
private readonly Lock _lock = new();
|
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<LogMessage> Logs => new(_logs);
|
|
||||||
|
|
||||||
public void Log(string message, LogLevel level)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
_logs.Add(new LogMessage(level, message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(Exception exception)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
_logs.Add(new LogMessage(LogLevel.Error, exception.Message, exception.StackTrace));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Assert(bool condition, string message)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (!condition)
|
|
||||||
{
|
|
||||||
Log(message, LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
_logs.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Logger
|
public static class Logger
|
||||||
{
|
{
|
||||||
private static readonly ILogger s_logger = new LoggerImplementation();
|
// TODO: Add file logging.
|
||||||
internal static ReadOnlyObservableCollection<LogMessage> Logs => s_logger.Logs;
|
private class LoggerImpl : ILogger
|
||||||
|
{
|
||||||
|
private readonly ObservableCollection<LogMessage> _logs = new();
|
||||||
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
|
public ReadOnlyObservableCollection<LogMessage> Logs => new(_logs);
|
||||||
|
|
||||||
|
public void Log(string message, LogLevel level)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_logs.Add(new LogMessage(level, message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log(Exception exception)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_logs.Add(new LogMessage(LogLevel.Error, exception.Message, exception.StackTrace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Assert(bool condition, string message)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (!condition)
|
||||||
|
{
|
||||||
|
Log(message, LogLevel.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_logs.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly ILogger s_logger = new LoggerImpl();
|
||||||
|
|
||||||
|
public static ReadOnlyObservableCollection<LogMessage> Logs => s_logger.Logs;
|
||||||
|
|
||||||
public static void Log(LogLevel level, object? message)
|
public static void Log(LogLevel level, object? message)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"Ghost.Graphics.Test (Package)": {
|
"Ghost.Graphics.Test (Package)": {
|
||||||
"commandName": "MsixPackage",
|
"commandName": "MsixPackage",
|
||||||
"nativeDebugging": true
|
"nativeDebugging": false
|
||||||
},
|
},
|
||||||
"Ghost.Graphics.Test (Unpackaged)": {
|
"Ghost.Graphics.Test (Unpackaged)": {
|
||||||
"commandName": "Project"
|
"commandName": "Project"
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Microsoft.UI.Xaml.Media;
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
|
||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.Test.Windows;
|
namespace Ghost.Graphics.Test.Windows;
|
||||||
|
|
||||||
public sealed partial class GraphicsTestWindow : Window
|
public sealed partial class GraphicsTestWindow : Window
|
||||||
{
|
{
|
||||||
private DispatcherTimer _resizeTimer;
|
|
||||||
private IRenderSystem? _renderSystem;
|
private IRenderSystem? _renderSystem;
|
||||||
private IRenderer? _renderer;
|
private IRenderer? _renderer;
|
||||||
private ISwapChain? _swapChain;
|
private ISwapChain? _swapChain;
|
||||||
|
|
||||||
private bool _isFirstActivationHandled;
|
private bool _isFirstActivationHandled;
|
||||||
private bool _isResizing;
|
|
||||||
|
|
||||||
public GraphicsTestWindow()
|
public GraphicsTestWindow()
|
||||||
{
|
{
|
||||||
@@ -24,11 +22,8 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
Activated += GraphicsTestWindow_Activated;
|
Activated += GraphicsTestWindow_Activated;
|
||||||
Closed += GraphicsTestWindow_Closed;
|
Closed += GraphicsTestWindow_Closed;
|
||||||
|
|
||||||
_resizeTimer = new DispatcherTimer();
|
|
||||||
_resizeTimer.Interval = TimeSpan.FromMilliseconds(200);
|
|
||||||
_resizeTimer.Tick += OnResizeTimerTick;
|
|
||||||
|
|
||||||
Panel.SizeChanged += SwapChainPanel_SizeChanged;
|
Panel.SizeChanged += SwapChainPanel_SizeChanged;
|
||||||
|
Panel.CompositionScaleChanged += SwapChainPanel_CompositionScaleChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GraphicsTestWindow_Activated(object sender, WindowActivatedEventArgs e)
|
private void GraphicsTestWindow_Activated(object sender, WindowActivatedEventArgs e)
|
||||||
@@ -39,7 +34,7 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AllocationManager.EnableDebugLayer();
|
Misaki.HighPerformance.LowLevel.Buffer.AllocationManager.EnableDebugLayer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_renderSystem = new RenderSystem(new RenderingConfig()
|
_renderSystem = new RenderSystem(new RenderingConfig()
|
||||||
@@ -52,11 +47,13 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
{
|
{
|
||||||
Width = (uint)AppWindow.Size.Width,
|
Width = (uint)AppWindow.Size.Width,
|
||||||
Height = (uint)AppWindow.Size.Height,
|
Height = (uint)AppWindow.Size.Height,
|
||||||
|
ScaleX = Panel.CompositionScaleX,
|
||||||
|
ScaleY = Panel.CompositionScaleY,
|
||||||
Format = TextureFormat.B8G8R8A8_UNorm,
|
Format = TextureFormat.B8G8R8A8_UNorm,
|
||||||
Target = SwapChainTarget.FromCompositionSurface(Panel)
|
Target = SwapChainTarget.FromCompositionSurface(Panel)
|
||||||
});
|
});
|
||||||
|
|
||||||
_renderer.SetRenderTarget(_swapChain.GetCurrentBackBuffer());
|
_renderer.RenderTargetStrategy = new SwapChainTargetStrategy(_swapChain);
|
||||||
|
|
||||||
_renderSystem.Start();
|
_renderSystem.Start();
|
||||||
CompositionTarget.Rendering += OnRendering;
|
CompositionTarget.Rendering += OnRendering;
|
||||||
@@ -75,27 +72,16 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
_renderSystem?.Dispose();
|
_renderSystem?.Dispose();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
AllocationManager.Dispose();
|
Misaki.HighPerformance.LowLevel.Buffer.AllocationManager.Dispose();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
{
|
{
|
||||||
//if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0)
|
if (_renderSystem == null || _swapChain == null)
|
||||||
//{
|
{
|
||||||
// _renderer?.RequestResize(new((uint)e.NewSize.Width, (uint)e.NewSize.Height));
|
return;
|
||||||
//}
|
}
|
||||||
|
|
||||||
_resizeTimer.Stop();
|
|
||||||
_resizeTimer.Start();
|
|
||||||
|
|
||||||
_isResizing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnResizeTimerTick(object? sender, object e)
|
|
||||||
{
|
|
||||||
_resizeTimer.Stop();
|
|
||||||
_isResizing = false;
|
|
||||||
|
|
||||||
var newWidth = (uint)(Panel.ActualWidth * Panel.CompositionScaleX);
|
var newWidth = (uint)(Panel.ActualWidth * Panel.CompositionScaleX);
|
||||||
var newHeight = (uint)(Panel.ActualHeight * Panel.CompositionScaleY);
|
var newHeight = (uint)(Panel.ActualHeight * Panel.CompositionScaleY);
|
||||||
@@ -105,8 +91,12 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderSystem?.WaitIdle();
|
_renderSystem.RequestSwapChainResize(_swapChain, new uint2(newWidth, newHeight));
|
||||||
_swapChain?.Resize(newWidth, newHeight);
|
}
|
||||||
|
|
||||||
|
private void SwapChainPanel_CompositionScaleChanged(SwapChainPanel sender, object args)
|
||||||
|
{
|
||||||
|
_swapChain?.SetScale(sender.CompositionScaleX, sender.CompositionScaleY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRendering(object? sender, object e)
|
private void OnRendering(object? sender, object e)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Ghost.Graphics.Contracts;
|
|||||||
|
|
||||||
public interface IRenderPass
|
public interface IRenderPass
|
||||||
{
|
{
|
||||||
public void Initialize(ref readonly RenderingContext ctx);
|
void Initialize(ref readonly RenderingContext ctx);
|
||||||
public void Execute(ref readonly RenderingContext ctx);
|
void Execute(ref readonly RenderingContext ctx);
|
||||||
public void Cleanup(IResourceDatabase resourceDatabase);
|
void Cleanup(IResourceDatabase resourceDatabase);
|
||||||
}
|
}
|
||||||
|
|||||||
30
Ghost.Graphics/Contracts/IRenderTargetStrategy.cs
Normal file
30
Ghost.Graphics/Contracts/IRenderTargetStrategy.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.Core;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
|
public interface IRenderTargetStrategy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a handle to the current render target texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A handle to the texture that is currently set as the render target.</returns>
|
||||||
|
Handle<Texture> GetRenderTarget();
|
||||||
|
/// <summary>
|
||||||
|
/// Begins a rendering operation using the specified command buffer. Typically this will include resource barriers,
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">The command buffer that records rendering commands.</param>
|
||||||
|
void BeginRender(ICommandBuffer cmd);
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes the rendering process using the specified command buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">The command buffer that contains the rendering commands to be finalized.</param>
|
||||||
|
void EndRender(ICommandBuffer cmd);
|
||||||
|
/// <summary>
|
||||||
|
/// Displays the current frame to the output device or screen.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Call this method after rendering operations to present the rendered content. The exact
|
||||||
|
/// behavior may depend on the underlying graphics implementation or device.</remarks>
|
||||||
|
void Present();
|
||||||
|
}
|
||||||
62
Ghost.Graphics/Core/RenderTargetStrategy.cs
Normal file
62
Ghost.Graphics/Core/RenderTargetStrategy.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
|
internal class SwapChainTargetStrategy : IRenderTargetStrategy
|
||||||
|
{
|
||||||
|
private readonly ISwapChain _swapChain;
|
||||||
|
|
||||||
|
public SwapChainTargetStrategy(ISwapChain swapChain)
|
||||||
|
{
|
||||||
|
_swapChain = swapChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<Texture> GetRenderTarget()
|
||||||
|
{
|
||||||
|
return _swapChain.GetCurrentBackBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginRender(ICommandBuffer cmd)
|
||||||
|
{
|
||||||
|
cmd.ResourceBarrier(GetRenderTarget().AsResource(), ResourceState.Present, ResourceState.RenderTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndRender(ICommandBuffer cmd)
|
||||||
|
{
|
||||||
|
cmd.ResourceBarrier(GetRenderTarget().AsResource(), ResourceState.RenderTarget, ResourceState.Present);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Present()
|
||||||
|
{
|
||||||
|
_swapChain.Present();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class TextureTargetStrategy : IRenderTargetStrategy
|
||||||
|
{
|
||||||
|
private readonly Handle<Texture> _texture;
|
||||||
|
|
||||||
|
public TextureTargetStrategy(Handle<Texture> texture)
|
||||||
|
{
|
||||||
|
_texture = texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Handle<Texture> GetRenderTarget()
|
||||||
|
{
|
||||||
|
return _texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BeginRender(ICommandBuffer cmd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndRender(ICommandBuffer cmd)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Present()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,28 +12,18 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
{
|
{
|
||||||
private readonly IGraphicsEngine _engine;
|
private readonly IGraphicsEngine _engine;
|
||||||
private readonly ICommandBuffer _directCmd;
|
private readonly ICommandBuffer _directCmd;
|
||||||
private readonly ICommandBuffer _copyCmd;
|
|
||||||
private readonly ICommandBuffer _computeCmd;
|
|
||||||
|
|
||||||
public ICommandBuffer DirectCommandBuffer => _directCmd;
|
public ICommandBuffer DirectCommandBuffer => _directCmd;
|
||||||
public ICommandBuffer CopyCommandBuffer => _copyCmd;
|
|
||||||
public ICommandBuffer ComputeCommandBuffer => _computeCmd;
|
|
||||||
|
|
||||||
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
|
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
|
||||||
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
||||||
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
||||||
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
||||||
|
|
||||||
internal RenderingContext(
|
internal RenderingContext(IGraphicsEngine engine, ICommandBuffer directCmd)
|
||||||
IGraphicsEngine engine,
|
|
||||||
ICommandBuffer directCmd,
|
|
||||||
ICommandBuffer copyCmd,
|
|
||||||
ICommandBuffer computeCmd)
|
|
||||||
{
|
{
|
||||||
_engine = engine;
|
_engine = engine;
|
||||||
_directCmd = directCmd;
|
_directCmd = directCmd;
|
||||||
_copyCmd = copyCmd;
|
|
||||||
_computeCmd = computeCmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICommandBuffer CrearteCommandBuffer(CommandBufferType type)
|
public ICommandBuffer CrearteCommandBuffer(CommandBufferType type)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.Core;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
|
|||||||
34
Ghost.Graphics/D3D12/D3D12CommandAllocator.cs
Normal file
34
Ghost.Graphics/D3D12/D3D12CommandAllocator.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Ghost.Core.Utilities;
|
||||||
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.LowLevel;
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
internal unsafe class D3D12CommandAllocator : ICommandAllocator
|
||||||
|
{
|
||||||
|
private UniquePtr<ID3D12CommandAllocator> _allocator;
|
||||||
|
|
||||||
|
public SharedPtr<ID3D12CommandAllocator> NativeAllocator => _allocator.Share();
|
||||||
|
|
||||||
|
public D3D12CommandAllocator(D3D12RenderDevice device, CommandBufferType type)
|
||||||
|
{
|
||||||
|
ID3D12CommandAllocator* pAllocator = default;
|
||||||
|
var commandListType = D3D12Utility.ToCommandListType(type);
|
||||||
|
|
||||||
|
device.NativeDevice.Get()->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
|
||||||
|
|
||||||
|
_allocator.Attach(pAllocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_allocator.Get()->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_allocator.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||||
{
|
{
|
||||||
private UniquePtr<ID3D12GraphicsCommandList10> _commandList;
|
private UniquePtr<ID3D12GraphicsCommandList10> _commandList;
|
||||||
private UniquePtr<ID3D12CommandAllocator> _allocator;
|
|
||||||
|
|
||||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
@@ -62,14 +61,11 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
_type = type;
|
_type = type;
|
||||||
|
|
||||||
ID3D12CommandAllocator* pAllocator = default;
|
|
||||||
ID3D12GraphicsCommandList10* pCommandList = default;
|
ID3D12GraphicsCommandList10* pCommandList = default;
|
||||||
var commandListType = ConvertCommandBufferType(type);
|
var commandListType = D3D12Utility.ToCommandListType(type);
|
||||||
|
|
||||||
device.NativeDevice.Get()->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
|
|
||||||
device.NativeDevice.Get()->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
|
device.NativeDevice.Get()->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
|
||||||
|
|
||||||
_allocator.Attach(pAllocator);
|
|
||||||
_commandList.Attach(pCommandList);
|
_commandList.Attach(pCommandList);
|
||||||
|
|
||||||
_pipelineLibrary = stateController;
|
_pipelineLibrary = stateController;
|
||||||
@@ -85,17 +81,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static D3D12_COMMAND_LIST_TYPE ConvertCommandBufferType(CommandBufferType type)
|
|
||||||
{
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
CommandBufferType.Graphics => D3D12_COMMAND_LIST_TYPE_DIRECT,
|
|
||||||
CommandBufferType.Compute => D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
|
||||||
CommandBufferType.Copy => D3D12_COMMAND_LIST_TYPE_COPY,
|
|
||||||
_ => throw new ArgumentException($"Unknown command buffer type: {type}")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void ThrowIfDisposed()
|
private void ThrowIfDisposed()
|
||||||
{
|
{
|
||||||
@@ -145,32 +130,28 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Begin()
|
public void Begin(ICommandAllocator allocator)
|
||||||
{
|
{
|
||||||
void ResetCommandList()
|
ThrowIfDisposed();
|
||||||
|
ThrowIfRecording();
|
||||||
|
|
||||||
|
if (allocator is not D3D12CommandAllocator d3d12Allocator)
|
||||||
{
|
{
|
||||||
ThrowIfFailed(_allocator.Get()->Reset());
|
throw new ArgumentException("Invalid command allocator type", nameof(allocator));
|
||||||
ThrowIfFailed(_commandList.Get()->Reset(_allocator.Get(), null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetBindlessHeap()
|
ThrowIfFailed(_commandList.Get()->Reset(d3d12Allocator.NativeAllocator, null));
|
||||||
|
|
||||||
|
if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
|
||||||
{
|
{
|
||||||
|
// Set descriptor heaps for bindless resources and samplers
|
||||||
|
|
||||||
var heaps = stackalloc ID3D12DescriptorHeap*[2];
|
var heaps = stackalloc ID3D12DescriptorHeap*[2];
|
||||||
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
|
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource heap
|
||||||
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
|
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler heap
|
||||||
_commandList.Get()->SetDescriptorHeaps(2, heaps);
|
_commandList.Get()->SetDescriptorHeaps(2, heaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThrowIfDisposed();
|
|
||||||
ThrowIfRecording();
|
|
||||||
|
|
||||||
ResetCommandList();
|
|
||||||
|
|
||||||
if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
|
|
||||||
{
|
|
||||||
SetBindlessHeap();
|
|
||||||
}
|
|
||||||
|
|
||||||
_commandCount = 0;
|
_commandCount = 0;
|
||||||
_isRecording = true;
|
_isRecording = true;
|
||||||
}
|
}
|
||||||
@@ -684,7 +665,6 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
}
|
}
|
||||||
|
|
||||||
_commandList.Dispose();
|
_commandList.Dispose();
|
||||||
_allocator.Dispose();
|
|
||||||
_commandCount = 0;
|
_commandCount = 0;
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#if DEBUG
|
||||||
|
#define ENABLE_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
@@ -11,7 +15,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
{
|
{
|
||||||
private readonly IRenderSystem _renderSystem;
|
private readonly IRenderSystem _renderSystem;
|
||||||
|
|
||||||
#if DEBUG
|
#if ENABLE_DEBUG
|
||||||
private readonly D3D12DebugLayer _debugLayer;
|
private readonly D3D12DebugLayer _debugLayer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -21,7 +25,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||||
private readonly D3D12CommandBuffer _copyCommandBuffer;
|
|
||||||
|
|
||||||
private ImmutableArray<IRenderer> _renderers;
|
private ImmutableArray<IRenderer> _renderers;
|
||||||
|
|
||||||
@@ -32,13 +35,12 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
||||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||||
public ICommandBuffer CopyCommandBuffer => _copyCommandBuffer;
|
|
||||||
|
|
||||||
public D3D12GraphicsEngine(IRenderSystem renderSystem)
|
public D3D12GraphicsEngine(IRenderSystem renderSystem)
|
||||||
{
|
{
|
||||||
_renderSystem = renderSystem;
|
_renderSystem = renderSystem;
|
||||||
|
|
||||||
#if DEBUG
|
#if ENABLE_DEBUG
|
||||||
_debugLayer = new D3D12DebugLayer();
|
_debugLayer = new D3D12DebugLayer();
|
||||||
#endif
|
#endif
|
||||||
_device = new D3D12RenderDevice();
|
_device = new D3D12RenderDevice();
|
||||||
@@ -49,14 +51,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
||||||
_resourceAllocator = new D3D12ResourceAllocator(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
_resourceAllocator = new D3D12ResourceAllocator(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
||||||
|
|
||||||
_copyCommandBuffer = new D3D12CommandBuffer(
|
|
||||||
_device,
|
|
||||||
_pipelineLibrary,
|
|
||||||
_resourceDatabase,
|
|
||||||
_resourceAllocator,
|
|
||||||
_descriptorAllocator,
|
|
||||||
CommandBufferType.Copy);
|
|
||||||
|
|
||||||
_renderers = ImmutableArray<IRenderer>.Empty;
|
_renderers = ImmutableArray<IRenderer>.Empty;
|
||||||
|
|
||||||
_pipelineLibrary.InitializeLibrary(null);
|
_pipelineLibrary.InitializeLibrary(null);
|
||||||
@@ -94,6 +88,11 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Clear());
|
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Clear());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics)
|
||||||
|
{
|
||||||
|
return new D3D12CommandAllocator(_device, type);
|
||||||
|
}
|
||||||
|
|
||||||
public ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
public ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
@@ -113,22 +112,27 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _renderSystem.MaxFrameLatency);
|
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _renderSystem.MaxFrameLatency);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RenderFrame(ICommandBuffer commandBuffer)
|
public Result RenderFrame(ICommandAllocator commandAllocator)
|
||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
|
|
||||||
_copyCommandBuffer.Begin();
|
var r = Result.Success();
|
||||||
|
|
||||||
foreach (var renderer in _renderers)
|
foreach (var renderer in _renderers)
|
||||||
{
|
{
|
||||||
renderer.Render(commandBuffer);
|
r = renderer.Render(commandAllocator);
|
||||||
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_copyCommandBuffer.End().ThrowIfFailed();
|
|
||||||
_resourceAllocator.ReleaseTempResources();
|
_resourceAllocator.ReleaseTempResources();
|
||||||
_descriptorAllocator.ResetCbvSrvUavDynamicHeap();
|
_descriptorAllocator.ResetCbvSrvUavDynamicHeap();
|
||||||
_descriptorAllocator.ResetDSVDynamicHeap();
|
_descriptorAllocator.ResetDSVDynamicHeap();
|
||||||
_descriptorAllocator.ResetRTVDynamicHeap();
|
_descriptorAllocator.ResetRTVDynamicHeap();
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@@ -138,8 +142,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_copyCommandBuffer.Dispose();
|
|
||||||
|
|
||||||
_resourceAllocator.Dispose();
|
_resourceAllocator.Dispose();
|
||||||
_pipelineLibrary.Dispose();
|
_pipelineLibrary.Dispose();
|
||||||
_resourceDatabase.Dispose();
|
_resourceDatabase.Dispose();
|
||||||
@@ -147,7 +149,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
|||||||
_descriptorAllocator.Dispose();
|
_descriptorAllocator.Dispose();
|
||||||
_shaderCompiler.Dispose();
|
_shaderCompiler.Dispose();
|
||||||
_device.Dispose();
|
_device.Dispose();
|
||||||
#if DEBUG
|
#if ENABLE_DEBUG
|
||||||
_debugLayer.Dispose();
|
_debugLayer.Dispose();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ internal unsafe class D3D12RenderDevice : IRenderDevice
|
|||||||
public ICommandQueue ComputeQueue => _computeQueue;
|
public ICommandQueue ComputeQueue => _computeQueue;
|
||||||
public ICommandQueue CopyQueue => _copyQueue;
|
public ICommandQueue CopyQueue => _copyQueue;
|
||||||
|
|
||||||
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Get();
|
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Share();
|
||||||
public SharedPtr<ID3D12Device14> NativeDevice => _device.Get();
|
public SharedPtr<ID3D12Device14> NativeDevice => _device.Share();
|
||||||
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Get();
|
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Share();
|
||||||
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue;
|
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue;
|
||||||
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue;
|
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue;
|
||||||
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue;
|
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
|||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Core;
|
||||||
using Ghost.Graphics.RenderPasses;
|
using Ghost.Graphics.RenderPasses;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
namespace Ghost.Graphics.D3D12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
@@ -10,48 +11,30 @@ namespace Ghost.Graphics.D3D12;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class D3D12Renderer : IRenderer
|
internal class D3D12Renderer : IRenderer
|
||||||
{
|
{
|
||||||
private struct FrameResource : IDisposable
|
|
||||||
{
|
|
||||||
public ICommandBuffer commandBuffer;
|
|
||||||
public ulong fenceValue;
|
|
||||||
|
|
||||||
public FrameResource(D3D12GraphicsEngine graphicsEngine, int index)
|
|
||||||
{
|
|
||||||
commandBuffer = graphicsEngine.CreateCommandBuffer();
|
|
||||||
commandBuffer.Name = $"Frame Command Buffer {index}";
|
|
||||||
fenceValue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly void Dispose()
|
|
||||||
{
|
|
||||||
commandBuffer?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly D3D12GraphicsEngine _graphicsEngine;
|
private readonly D3D12GraphicsEngine _graphicsEngine;
|
||||||
private uint _frameIndex;
|
|
||||||
|
|
||||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||||
|
|
||||||
private Handle<Texture> _renderTarget;
|
private readonly ICommandBuffer _commandBuffer;
|
||||||
|
|
||||||
|
private uint _frameIndex;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
// NOTE: Testing only.
|
// NOTE: Testing only.
|
||||||
private readonly MeshRenderPass _pass;
|
private readonly MeshRenderPass _pass;
|
||||||
|
|
||||||
public Handle<Texture> RenderTarget => _renderTarget;
|
public IRenderTargetStrategy? RenderTargetStrategy
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add render passes support
|
// TODO: Add render graph support
|
||||||
// private ImmutableArray<IRenderPass> _renderPasses;
|
|
||||||
|
|
||||||
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceDatabase resourceDatabase)
|
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceDatabase resourceDatabase)
|
||||||
{
|
{
|
||||||
_graphicsEngine = graphicsEngine;
|
_graphicsEngine = graphicsEngine;
|
||||||
_resourceDatabase = resourceDatabase;
|
_resourceDatabase = resourceDatabase;
|
||||||
|
|
||||||
_renderTarget = Handle<Texture>.Invalid;
|
_commandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Testing only.
|
// NOTE: Testing only.
|
||||||
_pass = new();
|
_pass = new();
|
||||||
@@ -62,36 +45,47 @@ internal class D3D12Renderer : IRenderer
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderTarget(Handle<Texture> renderTarget)
|
public Result Render(ICommandAllocator commandAllocator)
|
||||||
{
|
{
|
||||||
_resourceDatabase.ReleaseResource(_renderTarget.AsResource());
|
if (RenderTargetStrategy is null)
|
||||||
_renderTarget = renderTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Render(ICommandBuffer commandBuffer)
|
|
||||||
{
|
|
||||||
if (!_renderTarget.IsValid)
|
|
||||||
{
|
{
|
||||||
return Result.Failure("Render target is not set.");
|
return Result.Failure("Render target strategy is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
commandBuffer.Begin();
|
var target = RenderTargetStrategy.GetRenderTarget();
|
||||||
|
if (target.IsNotValid)
|
||||||
|
{
|
||||||
|
return Result.Failure("Render target is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_commandBuffer.Begin(commandAllocator);
|
||||||
|
RenderTargetStrategy.BeginRender(_commandBuffer);
|
||||||
|
|
||||||
// NOTE: Temperary solution: render directly to the swap chain back buffer if available.
|
// NOTE: Temperary solution: render directly to the swap chain back buffer if available.
|
||||||
// HACK: This is hard coded for testing purposes only.
|
// HACK: This is hard coded for testing purposes only.
|
||||||
|
|
||||||
var error = RenderScene(_renderTarget, commandBuffer);
|
var error = RenderScene(target);
|
||||||
if (error != ErrorStatus.None)
|
if (error != ErrorStatus.None)
|
||||||
{
|
{
|
||||||
commandBuffer.End();
|
_commandBuffer.End();
|
||||||
return Result.Failure(error);
|
return Result.Failure(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return commandBuffer.End();
|
RenderTargetStrategy.EndRender(_commandBuffer);
|
||||||
|
var r = _commandBuffer.End();
|
||||||
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
|
||||||
|
RenderTargetStrategy.Present();
|
||||||
|
|
||||||
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: A proper render graph integration.
|
// TODO: A proper render graph integration.
|
||||||
private ErrorStatus RenderScene(Handle<Texture> target, ICommandBuffer cmd)
|
private ErrorStatus RenderScene(Handle<Texture> target)
|
||||||
{
|
{
|
||||||
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
var clearColor = new Color128 { r = 1.0f, g = 0.0f, b = 1.0f, a = 1.0f };
|
||||||
|
|
||||||
@@ -112,7 +106,7 @@ internal class D3D12Renderer : IRenderer
|
|||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: Testing only.
|
// NOTE: Testing only.
|
||||||
var ctx = new RenderingContext(_graphicsEngine, cmd, _graphicsEngine.CopyCommandBuffer, null!);
|
var ctx = new RenderingContext(_graphicsEngine, _commandBuffer);
|
||||||
if (_frameIndex == 0)
|
if (_frameIndex == 0)
|
||||||
{
|
{
|
||||||
_pass.Initialize(ref ctx);
|
_pass.Initialize(ref ctx);
|
||||||
@@ -124,18 +118,20 @@ internal class D3D12Renderer : IRenderer
|
|||||||
return result.Error;
|
return result.Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Decouple viewport and scissor from the texture size.
|
||||||
var texDesc = result.Value.TextureDescription;
|
var texDesc = result.Value.TextureDescription;
|
||||||
var viewport = new ViewportDesc { Width = texDesc.Width, Height = texDesc.Height, MinDepth = 0, MaxDepth = 1 };
|
var viewport = new ViewportDesc { Width = texDesc.Width, Height = texDesc.Height, MinDepth = 0, MaxDepth = 1 };
|
||||||
var scissor = new RectDesc { Right = texDesc.Width, Bottom = texDesc.Height };
|
var scissor = new RectDesc { Right = texDesc.Width, Bottom = texDesc.Height };
|
||||||
|
|
||||||
cmd.BeginRenderPass(rtDesc, depthDesc, false);
|
_commandBuffer.BeginRenderPass(rtDesc, depthDesc, false);
|
||||||
cmd.SetViewport(viewport);
|
_commandBuffer.SetViewport(viewport);
|
||||||
cmd.SetScissorRect(scissor);
|
_commandBuffer.SetScissorRect(scissor);
|
||||||
|
|
||||||
// NOTE: Testing only.
|
// NOTE: Testing only.
|
||||||
_pass.Execute(ref ctx);
|
_pass.Execute(ref ctx);
|
||||||
|
|
||||||
cmd.EndRenderPass();
|
_commandBuffer.EndRenderPass();
|
||||||
|
_frameIndex++;
|
||||||
|
|
||||||
return ErrorStatus.None;
|
return ErrorStatus.None;
|
||||||
}
|
}
|
||||||
@@ -150,6 +146,8 @@ internal class D3D12Renderer : IRenderer
|
|||||||
// NOTE: Testing only.
|
// NOTE: Testing only.
|
||||||
_pass.Cleanup(_resourceDatabase);
|
_pass.Cleanup(_resourceDatabase);
|
||||||
|
|
||||||
|
_commandBuffer.Dispose();
|
||||||
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
private readonly DynamicArray<Shader?> _shaders; // NOTE: We use a simple list since shader is not frequently added/removed. This can save 4 bytes for each ecs component.
|
private readonly DynamicArray<Shader?> _shaders; // NOTE: We use a simple list since shader is not frequently added/removed. This can save 4 bytes for each ecs component.
|
||||||
private readonly Dictionary<ShaderPassKey, ShaderPass> _shaderPasses; // NOTE: The reason we use Dictionary here is that ShaderPassKey is a presistence identifier across multiple application sessions.
|
private readonly Dictionary<ShaderPassKey, ShaderPass> _shaderPasses; // NOTE: The reason we use Dictionary here is that ShaderPassKey is a presistence identifier across multiple application sessions.
|
||||||
|
|
||||||
private int _lastSamplerId;
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
public D3D12ResourceDatabase(D3D12DescriptorAllocator descriptorAllocator)
|
||||||
@@ -120,8 +119,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
|
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_shaders = new DynamicArray<Shader?>(16);
|
_shaders = new DynamicArray<Shader?>(16);
|
||||||
_shaderPasses = new Dictionary<ShaderPassKey, ShaderPass>(16);
|
_shaderPasses = new Dictionary<ShaderPassKey, ShaderPass>(16);
|
||||||
|
|
||||||
_lastSamplerId = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~D3D12ResourceDatabase()
|
~D3D12ResourceDatabase()
|
||||||
|
|||||||
@@ -42,6 +42,16 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float ScaleX
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float ScaleY
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount)
|
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount)
|
||||||
{
|
{
|
||||||
Debug.Assert(bufferCount >= 2);
|
Debug.Assert(bufferCount >= 2);
|
||||||
@@ -55,8 +65,11 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
Width = desc.Width;
|
Width = desc.Width;
|
||||||
Height = desc.Height;
|
Height = desc.Height;
|
||||||
|
|
||||||
CreateSwapChain(desc, bufferCount);
|
var pSwapChian = CreateSwapChain(desc, bufferCount);
|
||||||
|
_swapChain.Attach(pSwapChian);
|
||||||
|
|
||||||
CreateBackBuffers();
|
CreateBackBuffers();
|
||||||
|
SetScale(desc.ScaleX, desc.ScaleY);
|
||||||
|
|
||||||
_compositionSurface = desc.Target.CompositionSurface;
|
_compositionSurface = desc.Target.CompositionSurface;
|
||||||
}
|
}
|
||||||
@@ -66,7 +79,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateSwapChain(SwapChainDesc desc, uint bufferCount)
|
private IDXGISwapChain4* CreateSwapChain(SwapChainDesc desc, uint bufferCount)
|
||||||
{
|
{
|
||||||
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
|
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
|
||||||
{
|
{
|
||||||
@@ -124,7 +137,7 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
|
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
|
||||||
pTempSwapChain->Release();
|
pTempSwapChain->Release();
|
||||||
|
|
||||||
_swapChain.Attach(pSwapChain);
|
return pSwapChain;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateBackBuffers()
|
private void CreateBackBuffers()
|
||||||
@@ -155,6 +168,13 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ReadOnlySpan<Handle<Texture>> GetBackBuffers()
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
return _backBuffers.AsSpan();
|
||||||
|
}
|
||||||
|
|
||||||
public void Present(bool vsync = true)
|
public void Present(bool vsync = true)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
@@ -186,20 +206,27 @@ internal unsafe class D3D12SwapChain : ISwapChain
|
|||||||
Height = height;
|
Height = height;
|
||||||
|
|
||||||
CreateBackBuffers();
|
CreateBackBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
//float inverseScale = 1.0f / scale;
|
public void SetScale(float scaleX, float scaleY)
|
||||||
|
{
|
||||||
|
var inverseScaleX = 1.0f / scaleX;
|
||||||
|
var inverseScaleY = 1.0f / scaleY;
|
||||||
|
|
||||||
//DXGI_MATRIX_3X2_F inverseScaleMatrix = new DXGI_MATRIX_3X2_F
|
DXGI_MATRIX_3X2_F inverseScaleMatrix = new DXGI_MATRIX_3X2_F
|
||||||
//{
|
{
|
||||||
// _11 = inverseScale, // Scale X
|
_11 = inverseScaleX, // Scale X
|
||||||
// _22 = inverseScale, // Scale Y
|
_22 = inverseScaleY, // Scale Y
|
||||||
// _12 = 0.0f,
|
_12 = 0.0f,
|
||||||
// _21 = 0.0f,
|
_21 = 0.0f,
|
||||||
// _31 = 0.0f, // Offset X
|
_31 = 0.0f, // Offset X
|
||||||
// _32 = 0.0f // Offset Y
|
_32 = 0.0f // Offset Y
|
||||||
//};
|
};
|
||||||
|
|
||||||
//_swapChain.Get()->SetMatrixTransform(&inverseScaleMatrix);
|
_swapChain.Get()->SetMatrixTransform(&inverseScaleMatrix);
|
||||||
|
|
||||||
|
ScaleX = scaleX;
|
||||||
|
ScaleY = scaleY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -155,6 +155,17 @@ internal unsafe static class D3D12Utility
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static D3D12_COMMAND_LIST_TYPE ToCommandListType(CommandBufferType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
CommandBufferType.Graphics => D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||||
|
CommandBufferType.Compute => D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
||||||
|
CommandBufferType.Copy => D3D12_COMMAND_LIST_TYPE_COPY,
|
||||||
|
_ => throw new ArgumentException($"Unknown command buffer type: {type}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_CREATE(
|
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_CREATE(
|
||||||
D3D12_FILL_MODE fillMode,
|
D3D12_FILL_MODE fillMode,
|
||||||
|
|||||||
@@ -758,6 +758,15 @@ public struct SwapChainDesc
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float ScaleX
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float ScaleY
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Back buffer Format
|
/// Back buffer Format
|
||||||
|
|||||||
6
Ghost.Graphics/RHI/ICommandAllocator.cs
Normal file
6
Ghost.Graphics/RHI/ICommandAllocator.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
|
public interface ICommandAllocator : IDisposable
|
||||||
|
{
|
||||||
|
void Reset();
|
||||||
|
}
|
||||||
@@ -35,7 +35,7 @@ public interface ICommandBuffer : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Begins recording commands into this command buffer
|
/// Begins recording commands into this command buffer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Begin();
|
void Begin(ICommandAllocator allocator);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ends recording commands and prepares for submission
|
/// Ends recording commands and prepares for submission
|
||||||
|
|||||||
@@ -30,10 +30,32 @@ public interface IGraphicsEngine : IDisposable
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of an object that implements the IRenderer interface.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An object that provides rendering functionality through the IRenderer interface.</returns>
|
||||||
IRenderer CreateRenderer();
|
IRenderer CreateRenderer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the specified renderer from the collection of active renderers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="renderer">The renderer instance to remove. Cannot be null.</param>
|
||||||
void RemoveRenderer(IRenderer renderer);
|
void RemoveRenderer(IRenderer renderer);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all registered renderers from the collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Call this method to reset the renderer collection to an empty state. After calling this
|
||||||
|
/// method, no renderers will be available until new ones are added.</remarks>
|
||||||
void ClearRenderers();
|
void ClearRenderers();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new command allocator for the specified command buffer type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of command buffer for which to create the allocator. The default is CommandBufferType.Graphics.</param>
|
||||||
|
/// <returns>An <see cref="ICommandAllocator"/> instance configured for the specified command buffer type.</returns>
|
||||||
|
ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a command buffer for recording rendering commands
|
/// Creates a command buffer for recording rendering commands
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -51,5 +73,7 @@ public interface IGraphicsEngine : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renders the current frame.
|
/// Renders the current frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void RenderFrame(ICommandBuffer commandBuffer);
|
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||||
|
/// <returns>Result of the rendering operation</returns>
|
||||||
|
Result RenderFrame(ICommandAllocator commandAllocator);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.Core;
|
using Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
namespace Ghost.Graphics.RHI;
|
namespace Ghost.Graphics.RHI;
|
||||||
|
|
||||||
@@ -8,21 +8,15 @@ namespace Ghost.Graphics.RHI;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRenderer : IDisposable
|
public interface IRenderer : IDisposable
|
||||||
{
|
{
|
||||||
Handle<Texture> RenderTarget
|
IRenderTargetStrategy? RenderTargetStrategy
|
||||||
{
|
{
|
||||||
get;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the render Target for this renderer
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="renderTarget">Render Target to render into</param>
|
|
||||||
public void SetRenderTarget(Handle<Texture> renderTarget);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renders a frame
|
/// Renders a frame
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="commandBuffer">Command buffer to record rendering commands into</param>
|
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||||
/// <returns>Result of the rendering operation</returns>
|
/// <returns>Result of the rendering operation</returns>
|
||||||
public Result Render(ICommandBuffer commandBuffer);
|
Result Render(ICommandAllocator commandAllocator);
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ public interface ISwapChain : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the swap chain back buffers
|
/// Width of the swap chain back buffers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint Width
|
uint Width
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,23 @@ public interface ISwapChain : IDisposable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Height of the swap chain back buffers
|
/// Height of the swap chain back buffers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public uint Height
|
uint Height
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the horizontal scaling factor applied to the object. This is used for DPI scaling.
|
||||||
|
/// </summary>
|
||||||
|
float ScaleX
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the vertical scale factor applied to the object. This is used for DPI scaling.
|
||||||
|
/// </summary>
|
||||||
|
float ScaleY
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
@@ -28,18 +44,31 @@ public interface ISwapChain : IDisposable
|
|||||||
/// Gets the current back buffer texture
|
/// Gets the current back buffer texture
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Current back buffer texture</returns>
|
/// <returns>Current back buffer texture</returns>
|
||||||
public Handle<Texture> GetCurrentBackBuffer();
|
Handle<Texture> GetCurrentBackBuffer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all back buffer textures
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>All back buffer textures</returns>
|
||||||
|
ReadOnlySpan<Handle<Texture>> GetBackBuffers();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Presents the rendered frame
|
/// Presents the rendered frame
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="vsync">Enable vertical synchronization</param>
|
/// <param name="vsync">Enable vertical synchronization</param>
|
||||||
public void Present(bool vsync = true);
|
void Present(bool vsync = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resizes the swap chain back buffers
|
/// Resizes the swap chain back buffers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="width">New Width</param>
|
/// <param name="width">New Width</param>
|
||||||
/// <param name="height">New Height</param>
|
/// <param name="height">New Height</param>
|
||||||
public void Resize(uint width, uint height);
|
void Resize(uint width, uint height);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the horizontal and vertical scaling factors for the object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scaleX">The factor by which to scale the object along the X-axis.</param>
|
||||||
|
/// <param name="scaleY">The factor by which to scale the object along the Y-axis.</param>
|
||||||
|
void SetScale(float scaleX, float scaleY);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
|
using Misaki.HighPerformance.Mathematics;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ghost.Graphics;
|
namespace Ghost.Graphics;
|
||||||
|
|
||||||
@@ -61,6 +65,7 @@ public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
|||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
|
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -82,7 +87,7 @@ internal class RenderSystem : IRenderSystem
|
|||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
|
|
||||||
public required ICommandBuffer CommandBuffer
|
public required ICommandAllocator CommandAllocator
|
||||||
{
|
{
|
||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
@@ -96,7 +101,7 @@ internal class RenderSystem : IRenderSystem
|
|||||||
{
|
{
|
||||||
CpuReadyEvent.Dispose();
|
CpuReadyEvent.Dispose();
|
||||||
GpuReadyEvent.Dispose();
|
GpuReadyEvent.Dispose();
|
||||||
CommandBuffer.Dispose();
|
CommandAllocator.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +111,7 @@ internal class RenderSystem : IRenderSystem
|
|||||||
private readonly FrameResource[] _frameResources;
|
private readonly FrameResource[] _frameResources;
|
||||||
private readonly Thread _renderThread;
|
private readonly Thread _renderThread;
|
||||||
private readonly AutoResetEvent _shutdownEvent;
|
private readonly AutoResetEvent _shutdownEvent;
|
||||||
|
private readonly ConcurrentDictionary<ISwapChain, uint2> _resizeRequest;
|
||||||
|
|
||||||
private uint _frameIndex;
|
private uint _frameIndex;
|
||||||
private uint _cpuFenceValue;
|
private uint _cpuFenceValue;
|
||||||
@@ -131,8 +137,6 @@ internal class RenderSystem : IRenderSystem
|
|||||||
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.")
|
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.")
|
||||||
};
|
};
|
||||||
|
|
||||||
_shutdownEvent = new AutoResetEvent(false);
|
|
||||||
|
|
||||||
// Create frame resources for synchronization
|
// Create frame resources for synchronization
|
||||||
_frameResources = new FrameResource[config.FrameBufferCount];
|
_frameResources = new FrameResource[config.FrameBufferCount];
|
||||||
for (var i = 0; i < config.FrameBufferCount; i++)
|
for (var i = 0; i < config.FrameBufferCount; i++)
|
||||||
@@ -141,7 +145,7 @@ internal class RenderSystem : IRenderSystem
|
|||||||
{
|
{
|
||||||
CpuReadyEvent = new AutoResetEvent(false),
|
CpuReadyEvent = new AutoResetEvent(false),
|
||||||
GpuReadyEvent = new AutoResetEvent(true),
|
GpuReadyEvent = new AutoResetEvent(true),
|
||||||
CommandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics),
|
CommandAllocator = _graphicsEngine.CreateCommandAllocator(CommandBufferType.Graphics)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +156,9 @@ internal class RenderSystem : IRenderSystem
|
|||||||
Priority = ThreadPriority.Normal
|
Priority = ThreadPriority.Normal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_shutdownEvent = new AutoResetEvent(false);
|
||||||
|
_resizeRequest = new ConcurrentDictionary<ISwapChain, uint2>();
|
||||||
|
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
}
|
}
|
||||||
@@ -188,6 +195,12 @@ internal class RenderSystem : IRenderSystem
|
|||||||
_renderThread.Join();
|
_renderThread.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
_resizeRequest.AddOrUpdate(swapChain, newSize, (_, _) => newSize);
|
||||||
|
}
|
||||||
|
|
||||||
public bool WaitForGPUReady(int timeOut = -1)
|
public bool WaitForGPUReady(int timeOut = -1)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
@@ -243,13 +256,31 @@ internal class RenderSystem : IRenderSystem
|
|||||||
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
|
_graphicsEngine.Device.GraphicsQueue.WaitForValue(frameResource.FenceValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
_graphicsEngine.RenderFrame(frameResource.CommandBuffer);
|
if (!_resizeRequest.IsEmpty)
|
||||||
|
{
|
||||||
|
WaitIdle();
|
||||||
|
foreach (var kvp in _resizeRequest)
|
||||||
|
{
|
||||||
|
var swapChain = kvp.Key;
|
||||||
|
var newSize = kvp.Value;
|
||||||
|
swapChain.Resize(newSize.x, newSize.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = _graphicsEngine.RenderFrame(frameResource.CommandAllocator);
|
||||||
|
if (r.IsFailure)
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
#if DEBUG
|
||||||
|
System.Diagnostics.Debugger.Break();
|
||||||
|
#endif
|
||||||
|
Logger.LogError($"RenderFrame failed: {r.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
_gpuFenceValue++;
|
_gpuFenceValue++;
|
||||||
frameResource.GpuReadyEvent.Set();
|
|
||||||
|
|
||||||
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_frameIndex);
|
frameResource.GpuReadyEvent.Set();
|
||||||
_frameIndex++;
|
frameResource.FenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_gpuFenceValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user