From 261afa413332397ad0c2120cdc3da6669b7c0772 Mon Sep 17 00:00:00 2001 From: Misaki Date: Thu, 3 Jul 2025 23:23:46 +0900 Subject: [PATCH] Update rendering and resource management Changed the `EditorState` class to use a timeout in the `WaitForGPUReady` method for improved responsiveness. Changed the `nativeDebugging` setting in `launchSettings.json` to `false` for the "Ghost.Editor (Package)" profile. Changed the `D3D12Renderer` class to set the swap chain only for the composition target type and replaced back buffer reset with dispose. Changed the mapping of resources in `D3D12Resource` to use a pointer for improved safety and clarity. Changed the `Mesh` class's upload buffer creation to not use the `true` flag for better memory management. Added a new `Vertex` struct with a `StructLayout` attribute for improved interoperability with unmanaged code. Refactored the `GraphicsPipeline` class to replace `IsGpuReady` with `WaitForGPUReady`, including a timeout parameter. Added a constant buffer to the HLSL source code in `MeshRenderPass` for passing transformation matrices to the vertex shader. Expanded the `UnitTestAppWindow` class to include event handlers for window activation and size changes for better resource management. Updated the XAML for `UnitTestAppWindow` to include a `SwapChainPanel` and corrected the XML declaration for formatting consistency. --- Ghost.Editor/Core/AppState/EditorState.cs | 2 +- Ghost.Editor/Properties/launchSettings.json | 2 +- Ghost.Graphics/D3D12/D3D12Renderer.cs | 9 ++- Ghost.Graphics/D3D12/D3D12Resource.cs | 4 +- Ghost.Graphics/Data/Mesh.cs | 4 +- Ghost.Graphics/Data/Vertex.cs | 2 + Ghost.Graphics/GraphicsPipeline.cs | 9 +-- Ghost.Graphics/RenderPasses/MeshRenderPass.cs | 19 +++++- Ghost.UnitTest/Properties/launchSettings.json | 3 +- Ghost.UnitTest/UnitTestAppWindow.xaml | 13 ++-- Ghost.UnitTest/UnitTestAppWindow.xaml.cs | 61 ++++++++++++++++++- 11 files changed, 103 insertions(+), 25 deletions(-) diff --git a/Ghost.Editor/Core/AppState/EditorState.cs b/Ghost.Editor/Core/AppState/EditorState.cs index 0057571..4382619 100644 --- a/Ghost.Editor/Core/AppState/EditorState.cs +++ b/Ghost.Editor/Core/AppState/EditorState.cs @@ -64,7 +64,7 @@ internal class EditorState : IAppState private void OnRendering(object? sender, object e) { - if (GraphicsPipeline.IsGpuReady()) + if (GraphicsPipeline.WaitForGPUReady(0)) { _window?.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () => { diff --git a/Ghost.Editor/Properties/launchSettings.json b/Ghost.Editor/Properties/launchSettings.json index 1a7a357..8ac5828 100644 --- a/Ghost.Editor/Properties/launchSettings.json +++ b/Ghost.Editor/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Ghost.Editor (Package)": { "commandName": "MsixPackage", - "nativeDebugging": true + "nativeDebugging": false }, "Ghost.Editor (Unpackaged)": { "commandName": "Project" diff --git a/Ghost.Graphics/D3D12/D3D12Renderer.cs b/Ghost.Graphics/D3D12/D3D12Renderer.cs index 35bc468..33dbb53 100644 --- a/Ghost.Graphics/D3D12/D3D12Renderer.cs +++ b/Ghost.Graphics/D3D12/D3D12Renderer.cs @@ -130,7 +130,7 @@ internal unsafe class D3D12Renderer : IRenderer case SwapChainPresenter.TargetType.Hwnd: var swapChainFullscreenDesc = new SwapChainFullscreenDescription { - Windowed = false, + Windowed = true, }; _graphicsDevice.DXGIFactory.Ptr->CreateSwapChainForHwnd( @@ -150,7 +150,10 @@ internal unsafe class D3D12Renderer : IRenderer throw new InvalidOperationException("Failed to create IDXGISwapChain4 interface."); } - _swapChainPresenter.SwapChainPanelNative.SetSwapChain((IntPtr)_swapChain.Get()); + if (_swapChainPresenter.Type == SwapChainPresenter.TargetType.Composition) + { + _swapChainPresenter.SwapChainPanelNative.SetSwapChain((IntPtr)_swapChain.Get()); + } _backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex(); } @@ -221,7 +224,7 @@ internal unsafe class D3D12Renderer : IRenderer ref var frameResource = ref _frameResources[i]; if (frameResource.backBuffer.Get() is not null) { - frameResource.backBuffer.Reset(); + frameResource.backBuffer.Dispose(); _rtvHeap.ReleaseDescriptor(frameResource.backBufferDescriptorIndexes); } diff --git a/Ghost.Graphics/D3D12/D3D12Resource.cs b/Ghost.Graphics/D3D12/D3D12Resource.cs index d8da7b7..fa3f292 100644 --- a/Ghost.Graphics/D3D12/D3D12Resource.cs +++ b/Ghost.Graphics/D3D12/D3D12Resource.cs @@ -49,12 +49,14 @@ public unsafe class D3D12Resource : IResource fixed (T* ptr = data) { - var hr = _nativeResource.Get()->Map(0, &range, (void**)&ptr); + void* mappedPtr; + var hr = _nativeResource.Get()->Map(0, &range, &mappedPtr); if (hr.Failure) { var message = hr.ToString(); throw new InvalidOperationException($"Failed to map resource: {message}"); } + Unsafe.CopyBlock(mappedPtr, ptr, size); _nativeResource.Get()->Unmap(0, &range); } } diff --git a/Ghost.Graphics/Data/Mesh.cs b/Ghost.Graphics/Data/Mesh.cs index 066a72d..93e565b 100644 --- a/Ghost.Graphics/Data/Mesh.cs +++ b/Ghost.Graphics/Data/Mesh.cs @@ -220,8 +220,8 @@ public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialInde _vertexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(vertexBufferSize); _indexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(indexBufferSize); - var vertexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(vertexBufferSize, true); - var indexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(indexBufferSize, true); + var vertexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(vertexBufferSize, false); + var indexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(indexBufferSize, false); vertexUploadBuffer.SetData(_vertices.AsSpan()); indexUploadBuffer.SetData(_indices.AsSpan()); diff --git a/Ghost.Graphics/Data/Vertex.cs b/Ghost.Graphics/Data/Vertex.cs index 92ff10f..77e31c9 100644 --- a/Ghost.Graphics/Data/Vertex.cs +++ b/Ghost.Graphics/Data/Vertex.cs @@ -1,10 +1,12 @@ using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using Win32.Graphics.Dxgi.Common; namespace Ghost.Graphics.Data; +[StructLayout(LayoutKind.Sequential)] public struct Vertex(Vector4 position, Vector4 normal, Vector4 tangent, Color128 color, Vector4 uv) { public unsafe struct Semantic diff --git a/Ghost.Graphics/GraphicsPipeline.cs b/Ghost.Graphics/GraphicsPipeline.cs index 19eebf1..da3ed0e 100644 --- a/Ghost.Graphics/GraphicsPipeline.cs +++ b/Ghost.Graphics/GraphicsPipeline.cs @@ -118,13 +118,8 @@ public static class GraphicsPipeline } } - internal static bool IsGpuReady() - { - return _gpuFenceValue >= _cpuFenceValue; - } - - internal static void WaitForGPUReady() + internal static bool WaitForGPUReady(int timeOut = -1) { if (_gpuReadyEvent == null) { @@ -132,7 +127,7 @@ public static class GraphicsPipeline } var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT); - _gpuReadyEvent[eventIndex].WaitOne(); + return _gpuReadyEvent[eventIndex].WaitOne(timeOut); } internal static void SignalCPUReady() diff --git a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs index fc2bedc..9517b7a 100644 --- a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs +++ b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs @@ -4,6 +4,7 @@ using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.Data; using Ghost.Graphics.Utilities; using System.Drawing; +using System.Runtime.CompilerServices; using Win32; using Win32.Graphics.Direct3D; using Win32.Graphics.Direct3D12; @@ -14,6 +15,12 @@ namespace Ghost.Graphics.RenderPasses; internal unsafe class MeshRenderPass : IRenderPass { private const string _HLSL_SOURCE = @" + +cbuffer ConstantBuffer : register(b0) +{ + float4x4 WVP_Matrix; +}; + struct VertexInput { float3 position : POSITION; @@ -56,7 +63,17 @@ float4 PSMain(PixelInput input) : SV_TARGET private void CreateRootSignature() { - var rootSignatureDesc = new RootSignatureDescription(0u, null) + var rootParameters = new RootParameter[] + { + new () + { + ParameterType = RootParameterType.Cbv, + ShaderVisibility = ShaderVisibility.Vertex, + Descriptor = new RootDescriptor(0, 0) + } + }; + + var rootSignatureDesc = new RootSignatureDescription(0u, (RootParameter*)Unsafe.AsPointer(ref rootParameters[0])) { Flags = RootSignatureFlags.AllowInputAssemblerInputLayout }; diff --git a/Ghost.UnitTest/Properties/launchSettings.json b/Ghost.UnitTest/Properties/launchSettings.json index 04171d2..f54ed6e 100644 --- a/Ghost.UnitTest/Properties/launchSettings.json +++ b/Ghost.UnitTest/Properties/launchSettings.json @@ -1,7 +1,8 @@ { "profiles": { "Ghost.UnitTest (Package)": { - "commandName": "MsixPackage" + "commandName": "MsixPackage", + "nativeDebugging": true }, "Ghost.UnitTest (Unpackaged)": { "commandName": "Project" diff --git a/Ghost.UnitTest/UnitTestAppWindow.xaml b/Ghost.UnitTest/UnitTestAppWindow.xaml index c3c3bdc..9fc6315 100644 --- a/Ghost.UnitTest/UnitTestAppWindow.xaml +++ b/Ghost.UnitTest/UnitTestAppWindow.xaml @@ -1,19 +1,22 @@ - + + Title="Ghost.UnitTest" + mc:Ignorable="d"> - + diff --git a/Ghost.UnitTest/UnitTestAppWindow.xaml.cs b/Ghost.UnitTest/UnitTestAppWindow.xaml.cs index c459480..e792496 100644 --- a/Ghost.UnitTest/UnitTestAppWindow.xaml.cs +++ b/Ghost.UnitTest/UnitTestAppWindow.xaml.cs @@ -1,13 +1,68 @@ +using Ghost.Graphics; +using Ghost.Graphics.Contracts; using Microsoft.UI.Xaml; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. +using Microsoft.UI.Xaml.Media; +using Misaki.HighPerformance.Unsafe.Buffer; +using WinRT; namespace Ghost.UnitTest; + public sealed partial class UnitTestAppWindow : Window { + private IRenderer? _renderView; + private ISwapChainPanelNative _swapChainPanelNative; + public UnitTestAppWindow() { InitializeComponent(); + + Activated += UnitTestAppWindow_Activated; + Closed += UnitTestAppWindow_Closed; + + Panel.SizeChanged += SwapChainPanel_SizeChanged; + } + + private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e) + { + if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0) + { + _renderView?.RequestResize((uint)e.NewSize.Width, (uint)e.NewSize.Height); + } + } + + private void UnitTestAppWindow_Activated(object sender, WindowActivatedEventArgs args) + { + AllocationManager.Initialize(); + GraphicsPipeline.Initialize(Graphics.Data.GraphicsAPI.D3D12); + GraphicsPipeline.Start(); + + var guid = typeof(ISwapChainPanelNative.Interface).GUID; + ((IWinRTObject)Panel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle); + _swapChainPanelNative = new ISwapChainPanelNative(swapChainPanelNativeHandle); + + _renderView = GraphicsPipeline.GraphicsDevice.CreateRenderer(new(_swapChainPanelNative, (uint)AppWindow.Size.Width, (uint)AppWindow.Size.Height)); + + CompositionTarget.Rendering += OnRendering; + } + + private void UnitTestAppWindow_Closed(object sender, WindowEventArgs args) + { + GraphicsPipeline.SignalCPUReady(); + GraphicsPipeline.Shutdown(); + AllocationManager.Dispose(); + CompositionTarget.Rendering -= OnRendering; + _swapChainPanelNative.Dispose(); + _renderView?.Dispose(); + } + + private void OnRendering(object? sender, object e) + { + if (GraphicsPipeline.WaitForGPUReady(0)) + { + DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () => + { + GraphicsPipeline.SignalCPUReady(); + }); + } } }