diff --git a/Ghost.Core/Ghost.Core.csproj b/Ghost.Core/Ghost.Core.csproj index 5bc2b61..4b98d7f 100644 --- a/Ghost.Core/Ghost.Core.csproj +++ b/Ghost.Core/Ghost.Core.csproj @@ -10,17 +10,18 @@ True $(DefineConstants);PLATEFORME_WIN64 + True True $(DefineConstants);PLATEFORME_WIN64 + True - - + diff --git a/Ghost.Core/Graphics/ShaderDescriptor.cs b/Ghost.Core/Graphics/ShaderDescriptor.cs index 74e79ef..5baa4ff 100644 --- a/Ghost.Core/Graphics/ShaderDescriptor.cs +++ b/Ghost.Core/Graphics/ShaderDescriptor.cs @@ -10,11 +10,12 @@ public enum ShaderPropertyType { None, Float, Float2, Float3, Float4, + Float4x4, Int, Int2, Int3, Int4, UInt, UInt2, UInt3, UInt4, Bool, Bool2, Bool3, Bool4, - Texture2DBindless, Texture3DBindless, TextureCubeBindless, - Texture2DArrayBindless, TextureCubeArrayBindless, + Texture2D, Texture3D, TextureCube, + Texture2DArray, TextureCubeArray, } public struct ShaderEntryPoint @@ -77,11 +78,8 @@ public class FullPassDescriptor : IPassDescriptor public ShaderEntryPoint taskShader; public ShaderEntryPoint meshShader; public ShaderEntryPoint pixelShader; - public string? generatedCodePath; public List? defines; - public List? includes; public List? keywords; - public List? properties; public PipelineDescriptor localPipeline; public string Identifier => uniqueIdentifier; @@ -100,6 +98,42 @@ public class FallbackPassDescriptor : IPassDescriptor public class ShaderDescriptor { public string name = string.Empty; + public string? generatedCodePath; + public uint cbufferSize; public List globalProperties = new(); + public List properties = new(); public List passes = new(); -} \ No newline at end of file +} + +public static class ShaderDescriptorExtensions +{ + public static uint GetSize(this ShaderPropertyType type) + { + return type switch + { + ShaderPropertyType.Float => 4, + ShaderPropertyType.Float2 => 8, + ShaderPropertyType.Float3 => 12, + ShaderPropertyType.Float4 => 16, + ShaderPropertyType.Float4x4 => 64, + ShaderPropertyType.Int => 4, + ShaderPropertyType.Int2 => 8, + ShaderPropertyType.Int3 => 12, + ShaderPropertyType.Int4 => 16, + ShaderPropertyType.UInt => 4, + ShaderPropertyType.UInt2 => 8, + ShaderPropertyType.UInt3 => 12, + ShaderPropertyType.UInt4 => 16, + ShaderPropertyType.Bool => 4, + ShaderPropertyType.Bool2 => 8, + ShaderPropertyType.Bool3 => 12, + ShaderPropertyType.Bool4 => 16, + ShaderPropertyType.Texture2D => 4, // Bindless resource use uint32 + ShaderPropertyType.Texture3D => 4, + ShaderPropertyType.TextureCube => 4, + ShaderPropertyType.Texture2DArray => 4, + ShaderPropertyType.TextureCubeArray => 4, + _ => 0, + }; + } +} diff --git a/Ghost.Core/Logging.cs b/Ghost.Core/Logging.cs index 4358005..702749d 100644 --- a/Ghost.Core/Logging.cs +++ b/Ghost.Core/Logging.cs @@ -1,4 +1,5 @@ using System.Collections.ObjectModel; +using System.Diagnostics; namespace Ghost.Core; diff --git a/Ghost.Core/Result.cs b/Ghost.Core/Result.cs index 745c850..06184bf 100644 --- a/Ghost.Core/Result.cs +++ b/Ghost.Core/Result.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using TerraFX.Interop.Windows; namespace Ghost.Core; @@ -44,6 +43,11 @@ public readonly struct Result return new Result(value, status); } + public static RefResult CreateRef(ref T value, S status) + { + return new RefResult(ref value, status); + } + public override string ToString() => IsSuccess ? "OK" : $"Error: {Message}"; public static implicit operator bool(Result result) => result.IsSuccess; @@ -68,16 +72,6 @@ public readonly struct Result _message = message; } - public ref readonly T GetValueRef() - { - if (!IsSuccess) - { - throw new InvalidOperationException("Cannot get value from a failed Result."); - } - - return ref Unsafe.AsRef(in _value); - } - public static Result Success(T value) { return new Result(true, value); @@ -92,9 +86,7 @@ public readonly struct Result public static implicit operator Result(T? data) => data is not null ? Success(data) : Failure(null); public static implicit operator Result(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message); - public static implicit operator bool(Result result) => result.IsSuccess; - public static implicit operator Result(Result result) => result.IsSuccess ? Result.Success() : Result.Failure(result.Message); } public enum ResultStatus : byte @@ -126,11 +118,6 @@ public readonly struct Result _status = status; } - public ref readonly T GetValueRef() - { - return ref Unsafe.AsRef(in _value); - } - public static Result Create(T value, S status) { return new Result(value, status); @@ -139,6 +126,28 @@ public readonly struct Result public override string ToString() => $"Value: {_value}, Status: {_status}"; } +public readonly ref struct RefResult +{ + private readonly ref T _value; + private readonly S _status; + + public ref T Value => ref _value; + public S Status => _status; + + public RefResult(ref T value, S status) + { + _value = ref value; + _status = status; + } + + public static RefResult Create(ref T value, S status) + { + return new RefResult(ref value, status); + } + + public override string ToString() => $"Value: {_value}, Status: {_status}"; +} + public static class ResultExtensions { public static void ThrowIfFailed(this Result result) diff --git a/Ghost.Core/Utilities/Win32Utility.cs b/Ghost.Core/Utilities/Win32Utility.cs index 9bfa61a..4a3675d 100644 --- a/Ghost.Core/Utilities/Win32Utility.cs +++ b/Ghost.Core/Utilities/Win32Utility.cs @@ -32,14 +32,14 @@ internal static unsafe partial class Win32Utility public static Guid* IID_NULL { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in TerraFX.Interop.Windows.IID.IID_NULL)); + get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID.IID_NULL)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IID_PPV IID_PPV_ARGS(ComPtr comPtr) + public static IID_PPV IID_PPV_ARGS(ComPtr* comPtr) where T : unmanaged, IUnknown.Interface { - return new IID_PPV(Windows.__uuidof(), comPtr.PPV()); + return new IID_PPV(Windows.__uuidof(), (void**)comPtr); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -80,27 +80,6 @@ internal static unsafe partial class Win32Utility return Result.Failure($"{op} failed with code {hr}"); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid* IID(this ComPtr comPtr) - where T : unmanaged, IUnknown.Interface - { - return Windows.__uuidof(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid* IID(this T ptr) - where T : unmanaged, IUnknown.Interface - { - return Windows.__uuidof(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void** PPV(ref this ComPtr comPtr) - where T : unmanaged, IUnknown.Interface - { - return (void**)comPtr.GetAddressOf(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void** ReleaseAndGetVoidAddressOf(ref this ComPtr comPtr) where T : unmanaged, IUnknown.Interface diff --git a/Ghost.Editor/App.xaml b/Ghost.Editor/App.xaml index 32a4449..961b1c6 100644 --- a/Ghost.Editor/App.xaml +++ b/Ghost.Editor/App.xaml @@ -9,6 +9,7 @@ + diff --git a/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj b/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj index f9024f3..f0dbf77 100644 --- a/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj +++ b/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj @@ -55,7 +55,6 @@ - @@ -84,9 +83,14 @@ False True - False 10.0.20348.0 enable True + + + + + + diff --git a/Ghost.Graphics.Test/Properties/launchSettings.json b/Ghost.Graphics.Test/Properties/launchSettings.json index a2a54f9..ce2e5c5 100644 --- a/Ghost.Graphics.Test/Properties/launchSettings.json +++ b/Ghost.Graphics.Test/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Ghost.Graphics.Test (Package)": { "commandName": "MsixPackage", - "nativeDebugging": true + "nativeDebugging": false }, "Ghost.Graphics.Test (Unpackaged)": { "commandName": "Project" diff --git a/Ghost.Graphics.Test/Windows/GraphicsTestWindow.xaml.cs b/Ghost.Graphics.Test/Windows/GraphicsTestWindow.xaml.cs index c85909a..74e6ef0 100644 --- a/Ghost.Graphics.Test/Windows/GraphicsTestWindow.xaml.cs +++ b/Ghost.Graphics.Test/Windows/GraphicsTestWindow.xaml.cs @@ -1,15 +1,14 @@ -using Ghost.Graphics; using Ghost.Graphics.RHI; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; using Misaki.HighPerformance.LowLevel.Buffer; -using TerraFX.Interop.WinRT; -using WinRT; namespace Ghost.Graphics.Test.Windows; public sealed partial class GraphicsTestWindow : Window { + private bool _isFirstActivationHandled = false; + private IRenderSystem? _renderSystem; private IRenderer? _renderer; private ISwapChain? _swapChain; @@ -18,33 +17,46 @@ public sealed partial class GraphicsTestWindow : Window { InitializeComponent(); - Panel.Loaded += SwapChainPanel_Loaded; - Panel.Unloaded += SwapChainPanel_Unloaded; + Activated += GraphicsTestWindow_Activated; + Closed += GraphicsTestWindow_Closed; Panel.SizeChanged += SwapChainPanel_SizeChanged; } - private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e) + private void GraphicsTestWindow_Activated(object sender, WindowActivatedEventArgs e) { + if (_isFirstActivationHandled) + { + return; + } + #if DEBUG AllocationManager.EnableDebugLayer(); #endif - _renderSystem = new RenderSystem(new() + _renderSystem = new RenderSystem(new RenderingConfig() { FrameBufferCount = 2, GraphicsAPI = GraphicsAPI.Direct3D12 }); _renderer = _renderSystem.GraphicsEngine.CreateRenderer(); - - _swapChain = _renderSystem.GraphicsEngine.CreateSwapChain(new SwapChainDesc((uint)AppWindow.Size.Width, (uint)AppWindow.Size.Height, SwapChainTarget.FromCompositionSurface(Panel))); + + _swapChain = _renderSystem.GraphicsEngine.CreateSwapChain(new SwapChainDesc + { + Width = (uint)AppWindow.Size.Width, + Height = (uint)AppWindow.Size.Height, + Target = SwapChainTarget.FromCompositionSurface(Panel) + }); _renderer.SetSwapChain(_swapChain); _renderSystem.Start(); CompositionTarget.Rendering += OnRendering; + + e.Handled = true; + _isFirstActivationHandled = true; } - private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e) + private void GraphicsTestWindow_Closed(object sender, WindowEventArgs e) { CompositionTarget.Rendering -= OnRendering; _renderSystem?.Stop(); @@ -52,6 +64,10 @@ public sealed partial class GraphicsTestWindow : Window _renderer?.Dispose(); _swapChain?.Dispose(); _renderSystem?.Dispose(); + +#if DEBUG + AllocationManager.Dispose(); +#endif } private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e) @@ -69,7 +85,7 @@ public sealed partial class GraphicsTestWindow : Window return; } - if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.Config.FrameBufferCount) + if (_renderSystem.CPUFenceValue < _renderSystem.GPUFenceValue + _renderSystem.MaxFrameLatency) { DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () => { diff --git a/Ghost.Graphics/Contracts/IShaderCompiler.cs b/Ghost.Graphics/Contracts/IShaderCompiler.cs index edac3ca..6fedfa3 100644 --- a/Ghost.Graphics/Contracts/IShaderCompiler.cs +++ b/Ghost.Graphics/Contracts/IShaderCompiler.cs @@ -6,7 +6,7 @@ using Misaki.HighPerformance.LowLevel.Collections; namespace Ghost.Graphics.Contracts; -public struct CompileResult : IDisposable +public struct ShaderCompileResult : IDisposable { public UnsafeArray bytecode; public ShaderReflectionData reflectionData; @@ -21,9 +21,9 @@ public struct CompileResult : IDisposable public struct GraphicsCompiledResult : IDisposable { - public CompileResult tsResult; - public CompileResult msResult; - public CompileResult psResult; + public ShaderCompileResult tsResult; + public ShaderCompileResult msResult; + public ShaderCompileResult psResult; public void Dispose() { @@ -143,7 +143,7 @@ public readonly struct ShaderReflectionData public interface IShaderCompiler : IDisposable { - Result Compile(ref readonly CompilerConfig config, Allocator allocator); - Result CompilePass(IPassDescriptor descriptor); + Result Compile(ref readonly CompilerConfig config, Allocator allocator); + Result CompilePass(IPassDescriptor descriptor, string? generatedCodePath); Result LoadCompiledCache(ShaderPassKey key); } diff --git a/Ghost.Graphics/Core/Material.cs b/Ghost.Graphics/Core/Material.cs index 52b6647..d1faa70 100644 --- a/Ghost.Graphics/Core/Material.cs +++ b/Ghost.Graphics/Core/Material.cs @@ -2,8 +2,6 @@ using Ghost.Core; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.LowLevel.Utilities; -using Misaki.HighPerformance.Mathematics; using System.Runtime.CompilerServices; namespace Ghost.Graphics.Core; @@ -12,29 +10,38 @@ internal struct CBufferCache : IResourceReleasable { private UnsafeArray _cpuData; private Handle _gpuResource; + private uint _size; private uint _alignedSize; public readonly UnsafeArray CpuData => _cpuData; public readonly Handle GpuResource => _gpuResource; + public readonly uint Size => _size; public readonly uint AlignedSize => _alignedSize; - public readonly bool IsCreated => _gpuResource.IsValid && _cpuData.IsCreated; + public readonly bool IsCreated => _size != 0 && _gpuResource.IsValid && _cpuData.IsCreated; public CBufferCache(Handle buffer, uint bufferSize) { + _size = bufferSize; _alignedSize = (bufferSize + 255u) & ~255u; - _cpuData = new((int)AlignedSize, Allocator.Persistent); + _cpuData = new UnsafeArray((int)AlignedSize, Allocator.Persistent); _gpuResource = buffer; } public void ReleaseResource(IResourceDatabase database) { + if (!IsCreated) + { + return; + } + _cpuData.Dispose(); database.ReleaseResource(GpuResource.AsResource()); _gpuResource = Handle.Invalid; + _size = 0; _alignedSize = 0; } } @@ -42,183 +49,96 @@ internal struct CBufferCache : IResourceReleasable public struct Material : IResourceReleasable, IHandleType { private Identifier _shader; - private UnsafeArray _materialPropertiesCache; // One per shader pass + private CBufferCache _cBufferCache; + + internal readonly CBufferCache CBufferCache => _cBufferCache; public readonly Identifier Shader => _shader; - internal ref CBufferCache GetPassCache(int passIndex) - { - return ref _materialPropertiesCache[passIndex]; - } - - public void SetShader(Identifier shaderId, IResourceAllocator allocator, IResourceDatabase database) + public Result SetShader(Identifier shaderId, IResourceAllocator allocator, IResourceDatabase database) { if (!shaderId.IsValid) { - throw new ArgumentException("Shader ID is invalid."); + return Result.Failure("Shader ID is invalid."); } + _cBufferCache.ReleaseResource(database); _shader = shaderId; var shader = database.GetShaderReference(shaderId); - _materialPropertiesCache = new UnsafeArray(shader.PassCount, Allocator.Persistent); - for (var i = 0; i < shader.PassCount; i++) + if (shader.CBufferSize != 0) { - var pass = database.GetShaderPass(shader.GetPassKey(i)); - var cbufferInfo = pass.CBuffer; - - if (cbufferInfo.SizeInBytes == 0) - { - continue; - } - var desc = new BufferDesc { - Size = cbufferInfo.SizeInBytes, + Size = shader.CBufferSize, Usage = BufferUsage.Constant, MemoryType = ResourceMemoryType.Default, }; var buffer = allocator.CreateBuffer(ref desc); - _materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.SizeInBytes); - } - } - - void IResourceReleasable.ReleaseResource(IResourceDatabase database) - { - foreach (var cache in _materialPropertiesCache) - { - cache.ReleaseResource(database); + _cBufferCache = new CBufferCache(buffer, shader.CBufferSize); } - _materialPropertiesCache.Dispose(); - } -} - -public ref struct MaterialAccessor -{ - private ref Material _materialData; - private readonly Shader _shader; - - private readonly IResourceDatabase _resourceDatabase; - - public MaterialAccessor(Handle material, IResourceDatabase resourceDatabase) - { - _resourceDatabase = resourceDatabase; - - _materialData = ref resourceDatabase.GetMaterialReference(material); - _shader = resourceDatabase.GetShaderReference(_materialData.Shader); + return Result.Success(); } - private readonly unsafe void WriteToCache(string propertyName, in T value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly unsafe Result GetPropertyCache() where T : unmanaged { - foreach (var index in _shader.GetPropertyPassIndices(propertyName)) + if (sizeof(T) != _cBufferCache.Size) { - var passKey = _shader.GetPassKey(index); - var pass = _resourceDatabase.GetShaderPass(passKey); - var propertyInfo = pass.GetPropertyInfo(propertyName); - - if (propertyInfo.Size != sizeof(T)) - { - throw new ArgumentException($"Property '{propertyName}' has a size mismatch. Expected {propertyInfo.Size} bytes, but got {sizeof(T)} bytes."); - } - - ref var cache = ref _materialData.GetPassCache(index); - Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.StartOffset], value); - } - } - - /// - /// Sets a float property in the material's constant buffer. - /// - /// The name of the property to set. - /// The Value to set for the property. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void SetFloat(string propertyName, in float value) - { - WriteToCache(propertyName, in value); - } - - /// - /// Sets a uint property in the material's constant buffer (useful for texture indices). - /// - /// The name of the property to set. - /// The Value to set for the property. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void SetUInt(string propertyName, in uint value) - { - WriteToCache(propertyName, in value); - } - - /// - /// Sets a Vector property in the material's constant buffer. - /// - /// The name of the property to set. - /// The Value to set for the property. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void SetVector(string propertyName, in float4 value) - { - WriteToCache(propertyName, in value); - } - - /// - /// Sets a Matrix property in the material's constant buffer. - /// - /// The name of the property to set. - /// The Value to set for the property. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void SetMatrix(string propertyName, in float4x4 value) - { - WriteToCache(propertyName, in value); - } - - /// - /// Sets a texture index for a shader property (for bindless texture access) - /// - /// The name of the shader property (e.g., "_TextureIndex1") - /// The bindless texture to reference - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void SetTextureBindless(string propertyName, Handle texture) - { - var bindlessIndex = _resourceDatabase.GetBindlessIndex(texture.AsResource()); - if (bindlessIndex == -1) - { - throw new ArgumentException("The provided texture does not have a valid bindless index. Ensure the texture is created with bindless support."); + return Result.Create(default(T), ResultStatus.InvalidArgument); } - SetUInt(propertyName, (uint)bindlessIndex); + return Result.Create(*(T*)_cBufferCache.CpuData.GetUnsafePtr(), ResultStatus.Success); } - /// - /// Sets the mesh buffer indices for bindless vertex and index buffer access - /// - /// The mesh whose buffer indices to set - /// The name of the vertex buffer index property - /// The name of the index buffer index property [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void SetBufferBindless(string propertyName, Handle buffer) + public readonly Span GetRawPropertyCache() { - var bindlessIndex = _resourceDatabase.GetBindlessIndex(buffer.AsResource()); - if (bindlessIndex == -1) + if (_cBufferCache.Size == 0) { - throw new ArgumentException("The provided buffer does not have a valid bindless index. Ensure the buffer is created with bindless support."); + return Span.Empty; } - SetUInt(propertyName, (uint)bindlessIndex); + return _cBufferCache.CpuData.AsSpan(0, (int)_cBufferCache.Size); } - /// - /// Uploads all cached material data to the GPU using the specified command buffer. - /// - /// The command buffer used to perform the upload operations to the GPU. Cannot be null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void UploadMaterialData(ICommandBuffer cmb) + public readonly unsafe Result SetPropertyCache(ref readonly T data) + where T : unmanaged { - for (var i = 0; i < _shader.PassCount; i++) + if (sizeof(T) != _cBufferCache.Size) { - ref var cache = ref _materialData.GetPassCache(i); - cmb.UploadBuffer(cache.GpuResource, cache.CpuData.AsSpan()); + return new Result(false, ResultStatus.InvalidArgument); } + + Unsafe.WriteUnaligned(_cBufferCache.CpuData.GetUnsafePtr(), data); + return Result.Success(ResultStatus.Success); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly unsafe Result SetRawPropertyCache(ReadOnlySpan data) + { + if (data.Length != _cBufferCache.Size) + { + return new Result(false, ResultStatus.InvalidArgument); + } + + Unsafe.WriteUnaligned(_cBufferCache.CpuData.GetUnsafePtr(), data); + return Result.Success(ResultStatus.Success); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void UploadData(ICommandBuffer cmb) + { + cmb.UploadBuffer(_cBufferCache.GpuResource, _cBufferCache.CpuData.AsSpan()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void IResourceReleasable.ReleaseResource(IResourceDatabase database) + { + _cBufferCache.ReleaseResource(database); } } diff --git a/Ghost.Graphics/Core/Mesh.cs b/Ghost.Graphics/Core/Mesh.cs index 68f27bc..9c6bf54 100644 --- a/Ghost.Graphics/Core/Mesh.cs +++ b/Ghost.Graphics/Core/Mesh.cs @@ -11,6 +11,9 @@ namespace Ghost.Graphics.Core; public struct Mesh : IResourceReleasable, IHandleType { + private UnsafeList _vertices; + private UnsafeList _indices; + internal bool IsMeshDataDirty { get; private set; @@ -21,10 +24,10 @@ public struct Mesh : IResourceReleasable, IHandleType /// public UnsafeList Vertices { - readonly get => field; + readonly get => _vertices; set { - field = value; + _vertices = value; VertexCount = value.Count; IsMeshDataDirty = true; } @@ -35,10 +38,10 @@ public struct Mesh : IResourceReleasable, IHandleType /// public UnsafeList Indices { - readonly get => field; + readonly get => _indices; set { - field = value; + _indices = value; IndexCount = value.Count; IsMeshDataDirty = true; } @@ -100,8 +103,8 @@ public struct Mesh : IResourceReleasable, IHandleType internal Mesh(ReadOnlySpan vertices, ReadOnlySpan indices, Handle vertexBuffer, Handle indexBuffer) { - Vertices = new(vertices.Length, Allocator.Persistent); - Indices = new(indices.Length, Allocator.Persistent); + Vertices = new UnsafeList(vertices.Length, Allocator.Persistent); + Indices = new UnsafeList(indices.Length, Allocator.Persistent); Vertices.CopyFrom(vertices); Indices.CopyFrom(indices); VertexBuffer = vertexBuffer; @@ -112,8 +115,8 @@ public struct Mesh : IResourceReleasable, IHandleType public readonly void ReleaseCpuResources() { - Vertices.Dispose(); - Indices.Dispose(); + _vertices.Dispose(); + _indices.Dispose(); } void IResourceReleasable.ReleaseResource(IResourceDatabase database) diff --git a/Ghost.Graphics/Core/RenderingContext.cs b/Ghost.Graphics/Core/RenderingContext.cs index 14b6c9a..d11db14 100644 --- a/Ghost.Graphics/Core/RenderingContext.cs +++ b/Ghost.Graphics/Core/RenderingContext.cs @@ -56,13 +56,32 @@ public unsafe readonly ref struct RenderingContext queue.WaitIdle(); } - public Handle CreateMesh(UnsafeList vertices, UnsafeList indices) + public Handle CreateMesh(UnsafeList vertices, UnsafeList indices, bool staticMesh) { var mesh = ResourceAllocator.CreateMesh(vertices, indices); + ref var meshData = ref ResourceDatabase.GetMeshReference(mesh); + + var vertexHandle = meshData.VertexBuffer.AsResource(); + var indexHandle = meshData.IndexBuffer.AsResource(); + + _directCmd.ResourceBarrier(vertexHandle, ResourceState.Common, ResourceState.CopyDest); + _directCmd.ResourceBarrier(indexHandle, ResourceState.Common, ResourceState.CopyDest); + + _directCmd.UploadBuffer(meshData.VertexBuffer, meshData.Vertices.AsSpan()); + _directCmd.UploadBuffer(meshData.IndexBuffer, meshData.Indices.AsSpan()); + + _directCmd.ResourceBarrier(vertexHandle, ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer); + _directCmd.ResourceBarrier(indexHandle, ResourceState.CopyDest, ResourceState.IndexBuffer); + + if (staticMesh) + { + meshData.ReleaseCpuResources(); + } + return mesh; } - public Handle CreateMesh(ReadOnlySpan vertices, ReadOnlySpan indices) + public Handle CreateMesh(ReadOnlySpan vertices, ReadOnlySpan indices, bool staticMesh) { var vertexList = new UnsafeList(vertices.Length, Allocator.Persistent); var indexList = new UnsafeList(indices.Length, Allocator.Persistent); @@ -70,12 +89,7 @@ public unsafe readonly ref struct RenderingContext vertexList.CopyFrom(vertices); indexList.CopyFrom(indices); - return CreateMesh(vertexList, indexList); - } - - public MaterialAccessor GetMaterialAccessor(Handle material) - { - return new MaterialAccessor(material, ResourceDatabase); + return CreateMesh(vertexList, indexList, staticMesh); } // TODO: Make one memory pool for upload. @@ -108,12 +122,12 @@ public unsafe readonly ref struct RenderingContext if (needVertexTransition) { - _directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.VertexAndConstantBuffer); + _directCmd.ResourceBarrier(meshData.VertexBuffer.AsResource(), ResourceState.CopyDest, vertexState); } if (needIndexTransition) { - _directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, ResourceState.IndexBuffer); + _directCmd.ResourceBarrier(meshData.IndexBuffer.AsResource(), ResourceState.CopyDest, indexState); } if (markMeshStatic) @@ -177,7 +191,7 @@ public unsafe readonly ref struct RenderingContext slicePitch = slicePitch }; - _directCmd.UploadTexture(texture, subresourceData); + _directCmd.UploadTexture(texture, [subresourceData]); } if (needTransition) @@ -194,10 +208,15 @@ public unsafe readonly ref struct RenderingContext ref var materialRef = ref ResourceDatabase.GetMaterialReference(material); var shader = ResourceDatabase.GetShaderReference(materialRef.Shader); - shader.TryGetPassKey(passName, out var passIndex, out var passKey); + var keyResult = shader.TryGetPassKey(passName, out var passIndex); + if (keyResult.Status != ResultStatus.Success) + { + throw new Exception(keyResult.ToString()); + } + var hash = new GraphicsPipelineHash { - Id = passKey, + Id = keyResult.Value.Identifier, RtvCount = 1, DsvFormat = TextureFormat.Unknown, }; @@ -209,20 +228,13 @@ public unsafe readonly ref struct RenderingContext _directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer); // NOTE: We use fixed root signature layout for bindless rendering. - ref var cache = ref materialRef.GetPassCache(passIndex); + var cache = materialRef.CBufferCache; if (cache.IsCreated) { _directCmd.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource); } - // NOTE: Since we are using true bindless resources, we only need to set the descriptor heaps, not individual tables. - // TODO: Maybe handle the traditional bindless model? -#if false - var samplerGpuHandle = _descriptorAllocator.GetSamplerHeap()->GetGPUDescriptorHandleForHeapStart(); - _commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle); -#endif - - //var threadGroupCountX = ((uint)meshRef.IndexCount + numThreadsX - 1) / numThreadsX; - _directCmd.DispatchMesh(1, 1, 1); + var threadGroupCountX = ((uint)meshRef.IndexCount + numThreadsX - 1) / numThreadsX; + _directCmd.DispatchMesh(threadGroupCountX, 1, 1); } } \ No newline at end of file diff --git a/Ghost.Graphics/Core/Shader.cs b/Ghost.Graphics/Core/Shader.cs index eb353ad..9d56a77 100644 --- a/Ghost.Graphics/Core/Shader.cs +++ b/Ghost.Graphics/Core/Shader.cs @@ -3,45 +3,44 @@ using Ghost.Core.Graphics; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace Ghost.Graphics.Core; -public class ShaderPass : IResourceReleasable +public struct ShaderPass : IResourceReleasable { - private CBufferInfo _cbufferInfo; - // NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed. - private readonly Dictionary _propertyLookup; - - public CBufferInfo CBuffer => _cbufferInfo; - - public ShaderPass(CBufferInfo info) + public ShaderPassKey Identifier { - _cbufferInfo = info; - - var capacity = info.Properties?.Count ?? 0; - _propertyLookup = new Dictionary(capacity); - for (var i = 0; i < capacity; i++) - { - _propertyLookup[info.Properties![i].Name] = i; - } + get; init; } - public int GetPropertyId(string propertyName) + public ZTestOptions ZTest { - return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1; + get; set; } - public CBufferPropertyInfo GetPropertyInfo(int id) + public ZWriteOptions ZWrite { - return _cbufferInfo.Properties[id]; + get; set; } - public CBufferPropertyInfo GetPropertyInfo(string propertyName) + public CullOptions Cull { - return _cbufferInfo.Properties[GetPropertyId(propertyName)]; + get; set; } + public BlendOptions Blend + { + get; set; + } + + public uint ColorMask + { + get; set; + } + + // TODO: Shader variant. + void IResourceReleasable.ReleaseResource(IResourceDatabase database) { } @@ -52,46 +51,43 @@ public class ShaderPass : IResourceReleasable /// public class Shader : IResourceReleasable, IIdentifierType { - private UnsafeArray _passIDs; + private readonly uint _cbufferSize; + private UnsafeArray _passes; // TODO: Optmize lookups with a better data structure if needed private readonly Dictionary _passLookup; // pass name to index - private readonly Dictionary> _propertyLookup; // property name to pass index (property name to list of pass indices that contain the property) - public int PassCount => _passIDs.Count; + public int PassCount => _passes.Count; + public uint CBufferSize => _cbufferSize; internal Shader(ShaderDescriptor descriptor) { - _passIDs = new UnsafeArray(descriptor.passes.Count, Allocator.Persistent); - _passLookup = new(descriptor.passes.Count); - _propertyLookup = new(descriptor.passes.Count); + _cbufferSize = descriptor.cbufferSize; + _passes = new UnsafeArray(descriptor.passes.Count, Allocator.Persistent); + _passLookup = new Dictionary(descriptor.passes.Count); for (var i = 0; i < descriptor.passes.Count; i++) { var pass = descriptor.passes[i]; + + // TODO: Handle inherited passes + if (pass is not FullPassDescriptor fullPass) + { + continue; + } + var passKey = new ShaderPassKey(pass.Identifier); - _passIDs[i] = passKey; - _passLookup[pass.Name] = i; - - if (pass is FullPassDescriptor fullPass) + _passes[i] = new ShaderPass { - if (fullPass.properties == null) - { - continue; - } + Identifier = passKey, + ZTest = fullPass.localPipeline.zTest, + ZWrite = fullPass.localPipeline.zWrite, + Cull = fullPass.localPipeline.cull, + Blend = fullPass.localPipeline.blend, + ColorMask = fullPass.localPipeline.colorMask + }; - foreach (var property in fullPass.properties) - { - ref var passIndices = ref CollectionsMarshal.GetValueRefOrAddDefault(_propertyLookup, property.name, out var exists); - if (!exists || passIndices == null) - { - passIndices = new List(); - } - - passIndices.Add(i); - } - } - // TODO: handle inherited passes + _passLookup[pass.Name] = i; } } @@ -100,38 +96,26 @@ public class Shader : IResourceReleasable, IIdentifierType return _passLookup.GetValueOrDefault(passName, -1); } - public ShaderPassKey GetPassKey(int index) + public ref ShaderPass GetPassReference(int index) { - return _passIDs[index]; + return ref _passes[index]; } - public bool TryGetPassKey(string passName, out int passIndex, out ShaderPassKey passID) + public RefResult TryGetPassKey(string passName, out int passIndex) { var index = _passLookup.GetValueOrDefault(passName, -1); if (index == -1) { passIndex = -1; - passID = new(0); - return false; + return Result.CreateRef(ref Unsafe.NullRef(), ResultStatus.NotFound); } passIndex = index; - passID = _passIDs[index]; - return true; - } - - public IReadOnlyCollection GetPropertyPassIndices(string propertyName) - { - if (_propertyLookup.TryGetValue(propertyName, out var passIndices)) - { - return passIndices; - } - - return Array.Empty(); + return Result.CreateRef(ref _passes[index], ResultStatus.Success); } void IResourceReleasable.ReleaseResource(IResourceDatabase database) { - _passIDs.Dispose(); + _passes.Dispose(); } } diff --git a/Ghost.Graphics/Core/Vertex.cs b/Ghost.Graphics/Core/Vertex.cs index aaacf45..b47b066 100644 --- a/Ghost.Graphics/Core/Vertex.cs +++ b/Ghost.Graphics/Core/Vertex.cs @@ -9,7 +9,7 @@ namespace Ghost.Graphics.Core; [StructLayout(LayoutKind.Sequential)] public struct Vertex { - public unsafe static class Semantic + public static class Semantic { public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT; public const int COUNT = 5; diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs index 6f47fef..08e5dfd 100644 --- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs +++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs @@ -173,17 +173,65 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer _commandList.Get()->RSSetScissorRects(1, &d3d12Rect); } - public void ResourceBarrier(Handle resource, ResourceState before, ResourceState after) + public void ResourceBarrier(ReadOnlySpan barrierDescs) { ThrowIfDisposed(); ThrowIfNotRecording(); IncrementCommandCount(); - var d3d12Resource = _resourceDatabase.GetResource(resource); - var barrier = D3D12_RESOURCE_BARRIER.InitTransition(d3d12Resource, - before.ToD3D12States(), after.ToD3D12States()); + var count = 0u; + var pBarriers = stackalloc D3D12_RESOURCE_BARRIER[barrierDescs.Length]; + + for (int i = 0; i < barrierDescs.Length; i++) + { + var desc = barrierDescs[i]; + if (desc.StateBefore == desc.StateAfter) + { + continue; + } + + if (!desc.Resource.IsValid) + { + throw new ArgumentException($"Barrier resource at index {i} is not a valid resource handle"); + } + + ref var resourceRecord = ref _resourceDatabase.GetResourceRecord(desc.Resource.AsResource()); + if (resourceRecord.state != desc.StateBefore) + { + throw new InvalidOperationException($"Resource state mismatch: expected {desc.StateBefore}, actual {resourceRecord.state}"); + } + + var barrier = D3D12_RESOURCE_BARRIER.InitTransition(resourceRecord.ResourcePtr, + desc.StateBefore.ToD3D12States(), desc.StateAfter.ToD3D12States()); + + pBarriers[count] = barrier; + count++; + + // Update the resource state in the database + resourceRecord.state = desc.StateAfter; + } + + _commandList.Get()->ResourceBarrier(count, pBarriers); + } + + public void ResourceBarrier(Handle resource, ResourceState stateBefore, ResourceState stateAfter) + { + if (stateBefore == stateAfter) + { + return; + } + + ref var resourceRecord = ref _resourceDatabase.GetResourceRecord(resource); + if (resourceRecord.state != stateBefore) + { + throw new InvalidOperationException($"Resource state mismatch: expected {stateBefore}, actual {resourceRecord.state}"); + } + + var barrier = D3D12_RESOURCE_BARRIER.InitTransition(resourceRecord.ResourcePtr, + stateBefore.ToD3D12States(), stateAfter.ToD3D12States()); _commandList.Get()->ResourceBarrier(1, &barrier); + resourceRecord.state = stateAfter; } public void SetRenderTargets(ReadOnlySpan> renderTargets, Handle depthTarget) @@ -465,7 +513,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer _commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, 0, sizeInBytes); } - public void UploadTexture(Handle texture, params ReadOnlySpan subresources) + public void UploadTexture(Handle texture, ReadOnlySpan subresources) { ThrowIfDisposed(); ThrowIfNotRecording(); diff --git a/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs b/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs index 35fa603..7c4dfcb 100644 --- a/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs +++ b/Ghost.Graphics/D3D12/D3D12DescriptorAllocator.cs @@ -19,7 +19,7 @@ internal unsafe class D3D12DescriptorAllocator : IDisposable private bool _disposed; - public unsafe D3D12DescriptorAllocator(D3D12RenderDevice device, int initialRtvCount = 256, int initialDsvCount = 256, int initialSrvCount = 200_000, int initialSamplerCount = 256) + public D3D12DescriptorAllocator(D3D12RenderDevice device, int initialRtvCount = 256, int initialDsvCount = 256, int initialSrvCount = 200_000, int initialSamplerCount = 256) { _rtvHeap = new D3D12DescriptorHeap("rtv", device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, initialRtvCount, initialRtvCount / 2); _dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount, initialDsvCount / 2); diff --git a/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs b/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs index 4fecc9c..f87700a 100644 --- a/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs +++ b/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; namespace Ghost.Graphics.D3D12; -internal unsafe class D3D12GraphicsEngine : IGraphicsEngine +internal class D3D12GraphicsEngine : IGraphicsEngine { #if DEBUG private readonly D3D12DebugLayer _debugLayer; @@ -34,17 +34,17 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine public D3D12GraphicsEngine(IRenderSystem renderSystem) { #if DEBUG - _debugLayer = new(); + _debugLayer = new D3D12DebugLayer(); #endif - _device = new(); - _shaderCompiler = new(); - _descriptorAllocator = new(_device); + _device = new D3D12RenderDevice(); + _shaderCompiler = new DxcShaderCompiler(); + _descriptorAllocator = new D3D12DescriptorAllocator(_device); - _resourceDatabase = new(_descriptorAllocator); - _pipelineLibrary = new(_device, _resourceDatabase); - _resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary); + _resourceDatabase = new D3D12ResourceDatabase(_descriptorAllocator); + _pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase); + _resourceAllocator = new D3D12ResourceAllocator(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary); - _copyCommandBuffer = new( + _copyCommandBuffer = new D3D12CommandBuffer( _device, _pipelineLibrary, _resourceDatabase, @@ -136,6 +136,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine _copyCommandBuffer.End(); _resourceAllocator.ReleaseTempResources(); + _descriptorAllocator.ResetCbvSrvUavDynamicHeap(); + _descriptorAllocator.ResetDSVDynamicHeap(); + _descriptorAllocator.ResetRTVDynamicHeap(); } public void Dispose() diff --git a/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs b/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs index 3116caa..f911ee7 100644 --- a/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs +++ b/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs @@ -8,8 +8,6 @@ using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.LowLevel.Utilities; -using Misaki.HighPerformance.Utilities; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -40,7 +38,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary private UniquePtr _defaultRootSignature; private readonly Dictionary _pipelineCache; - private readonly Dictionary _cbufferInfoCache; public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get(); @@ -50,9 +47,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary _resourceDatabase = resourceDatabase; _pipelineCache = new Dictionary(); - _cbufferInfoCache = new Dictionary(); - CreateDefaultRootSignature(); + CreateDefaultRootSignature().ThrowIfFailed(); } private Result CreateDefaultRootSignature() @@ -140,36 +136,21 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary Desc_1_1 = rootSignatureDesc }; - ID3DBlob* pSignature = default; - ID3DBlob* pError = default; + using ComPtr pSignature = default; + using ComPtr pError = default; - try + var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, pSignature.GetAddressOf(), pError.GetAddressOf()); + if (serializeResult.FAILED) { - var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, &pSignature, &pError); - if (serializeResult.FAILED) - { - var errorMsg = pError != null ? Marshal.PtrToStringUTF8((nint)pError->GetBufferPointer()) : "Unknown error"; - return Result.Failure($"Failed to serialize default root signature: {errorMsg}"); - } - - ID3D12RootSignature* pRootSignature = default; - ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature->GetBufferPointer(), pSignature->GetBufferSize(), - __uuidof(pRootSignature), (void**)&pRootSignature)); - - _defaultRootSignature.Attach(pRootSignature); + var errorMsg = pError.Get() != null ? Marshal.PtrToStringUTF8((nint)pError.Get()->GetBufferPointer()) : "Unknown error"; + return Result.Failure($"Failed to serialize default root signature: {errorMsg}"); } - finally - { - if (pSignature != null) - { - pSignature->Release(); - } - if (pError != null) - { - pError->Release(); - } - } + ID3D12RootSignature* pRootSignature = default; + ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature.Get()->GetBufferPointer(), pSignature.Get()->GetBufferSize(), + __uuidof(pRootSignature), (void**)&pRootSignature)); + + _defaultRootSignature.Attach(pRootSignature); return Result.Success(); } @@ -337,8 +318,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary return Result.Failure(result.Message); } - _cbufferInfoCache[descriptor.PassId] = result.Value; - var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC { pRootSignature = _defaultRootSignature.Get(), @@ -422,16 +401,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary return key; } - public Result GetCBufferInfo(ShaderPassKey passId) - { - if (_cbufferInfoCache.TryGetValue(passId, out var cbufferInfo)) - { - return Result.Create(cbufferInfo, ResultStatus.Success); - } - - return Result.Create(default(CBufferInfo), ResultStatus.NotFound); - } - public Result, ResultStatus> GetGraphicsPSO(GraphicsPipelineKey key) { if (_pipelineCache.TryGetValue(key, out var cacheEntry)) diff --git a/Ghost.Graphics/D3D12/D3D12Renderer.cs b/Ghost.Graphics/D3D12/D3D12Renderer.cs index 14784df..223bd7e 100644 --- a/Ghost.Graphics/D3D12/D3D12Renderer.cs +++ b/Ghost.Graphics/D3D12/D3D12Renderer.cs @@ -31,7 +31,6 @@ internal class D3D12Renderer : IRenderer } private readonly D3D12GraphicsEngine _graphicsEngine; - private readonly D3D12CommandQueue _commandQueue; private readonly FrameResource[] _frameResources; private uint _frameIndex; @@ -60,7 +59,6 @@ internal class D3D12Renderer : IRenderer public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceAllocator resourceAllocator, D3D12ResourceDatabase resourceDatabase) { _graphicsEngine = graphicsEngine; - _commandQueue = (D3D12CommandQueue)graphicsEngine.Device.GraphicsQueue; _resourceAllocator = resourceAllocator; _resourceDatabase = resourceDatabase; @@ -153,7 +151,7 @@ internal class D3D12Renderer : IRenderer _swapChain?.Resize(newSize.x, newSize.y); _currentSize = newSize; - // Update off-screen render target size + // Update off-screen render Target size if (_swapChain != null) { _resourceDatabase.ReleaseResource(_renderTarget.AsResource()); @@ -170,7 +168,7 @@ internal class D3D12Renderer : IRenderer if (frame.fenceValue > 0) { - _commandQueue.WaitForValue(frame.fenceValue); + _graphicsEngine.Device.GraphicsQueue.WaitForValue(frame.fenceValue); } if (_renderTarget.IsValid) @@ -202,12 +200,12 @@ internal class D3D12Renderer : IRenderer frame.commandBuffer.End(); - _commandQueue.Submit(frame.commandBuffer); + _graphicsEngine.Device.GraphicsQueue.Submit(frame.commandBuffer); _swapChain?.Present(); } - frame.fenceValue = _commandQueue.Signal(_frameIndex); + frame.fenceValue = _graphicsEngine.Device.GraphicsQueue.Signal(_frameIndex); _frameIndex++; } @@ -257,7 +255,7 @@ internal class D3D12Renderer : IRenderer // Handle swap chain back buffer transitions if needed if (_swapChain != null) { - // Transition back buffer to render target + // Transition back buffer to render Target cmd.ResourceBarrier(destination.AsResource(), ResourceState.Present, ResourceState.RenderTarget); } @@ -266,7 +264,7 @@ internal class D3D12Renderer : IRenderer // FIX: Implement proper blit operation with shader // This is a placeholder - in D3D12, you would typically: - // 1. Set render target to the destination + // 1. Set render Target to the destination // 2. Use a full-screen quad/triangle with a shader that samples from the source // Handle swap chain back buffer transitions if needed @@ -284,7 +282,7 @@ internal class D3D12Renderer : IRenderer { if (frame.fenceValue > 0) { - _commandQueue.WaitForValue(frame.fenceValue); + _graphicsEngine.Device.GraphicsQueue.WaitForValue(frame.fenceValue); } } } @@ -301,8 +299,8 @@ internal class D3D12Renderer : IRenderer // NOTE: Testing only. _pass.Cleanup(_resourceDatabase); - // If using a swap chain, release the off-screen render target. - // Otherwise, the render target is managed externally. + // If using a swap chain, release the off-screen render Target. + // Otherwise, the render Target is managed externally. if (_swapChain != null) { _resourceDatabase.ReleaseResource(_renderTarget.AsResource()); diff --git a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs index fc33db4..a5433c4 100644 --- a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs +++ b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs @@ -5,7 +5,7 @@ using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel.Collections; -using Misaki.HighPerformance.Mathematics; +using System.Diagnostics; using System.Runtime.CompilerServices; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -522,10 +522,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator return D3D12_RESOURCE_STATE_COPY_DEST; } - // Default to Common, but check for specific roles var state = D3D12_RESOURCE_STATE_COMMON; +#if true return state; - +#else + // D3D12 does not support state other than COMMON for buffers at creation. if (usage.HasFlag(BufferUsage.Vertex) || usage.HasFlag(BufferUsage.Constant)) { // Vertex and Constant buffers can share this state @@ -564,6 +565,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator // If it's a mix, start in common and let the user barrier return D3D12_RESOURCE_STATE_COMMON; +#endif } private static ResourceState D3D12StatesToRHIState(D3D12_RESOURCE_STATES states) @@ -605,7 +607,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator private readonly D3D12ResourceDatabase _resourceDatabase; private readonly D3D12PipelineLibrary _pipelineLibrary; - private UnsafeQueue> _temResources; + private UnsafeQueue> _tempResources; private bool _disposed; @@ -633,7 +635,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator _resourceDatabase = resourceDatabase; _pipelineLibrary = pipelineLibrary; - _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); + _tempResources = new UnsafeQueue>(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); } ~D3D12ResourceAllocator() @@ -648,7 +650,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator if (isTemp) { - _temResources.Enqueue(handle); + _tempResources.Enqueue(handle); } return handle; @@ -714,13 +716,15 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator var initialState = DetermineInitialTextureState(desc.Usage); D3D12MA_Allocation* pAllocation = default; - ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, Win32Utility.IID_NULL, null)); + var iid = IID.IID_NULL; + ThrowIfFailed(_d3d12MA.Get()->CreateResource(&allocationDesc, &resourceDesc, initialState, null, &pAllocation, &iid, null)); var resourceDescriptor = ResourceViewGroup.Invalid; if (desc.Usage.HasFlag(TextureUsage.ShaderResource)) { resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav(isTemp); - var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv); + // TODO: Maybe use non-shader-visible descriptor first then batch copy to shader-visible heap later? + var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.srv); var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray; var srvDesc = CreateTextureSrvDesc(pAllocation->GetResource(), mipLevels, desc.Slice, isCubeMap); @@ -749,7 +753,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess)) { resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav(isTemp); - var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav); + var cpuHandle = _descriptorAllocator.GetCpuHandleShaderVisible(resourceDescriptor.uav); var uavDesc = CreateTextureUavDesc(pAllocation->GetResource()); _device.NativeDevice.Get()->CreateUnorderedAccessView(pAllocation->GetResource(), null, &uavDesc, cpuHandle); @@ -906,24 +910,6 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator ObjectDisposedException.ThrowIf(_disposed, this); var shader = new Shader(descriptor); - foreach (var pass in descriptor.passes) - { - if (pass is not FullPassDescriptor fullPass) - { - continue; - } - - // TODO: Cache the pass key because we hash it multiple times right now. - var passKey = new ShaderPassKey(fullPass.Identifier); - var cbr = _pipelineLibrary.GetCBufferInfo(passKey); - if (cbr.Status != ResultStatus.Success) - { - return Identifier.Invalid; - } - - _resourceDatabase.AddShaderPass(passKey, new ShaderPass(cbr.Value)); - } - return _resourceDatabase.AddShader(shader); } @@ -931,14 +917,14 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator { ObjectDisposedException.ThrowIf(_disposed, this); - while (_temResources.Count > 0) + while (_tempResources.Count > 0) { - var handle = _temResources.Peek(); - ref var info = ref _resourceDatabase.GetResourceInfo(handle, out var exist); + var handle = _tempResources.Peek(); + ref var info = ref _resourceDatabase.GetResourceRecord(handle, out var exist); if (!exist || !info.Allocated) { // Resource already released or invalid, just dequeue - _temResources.Dequeue(); + _tempResources.Dequeue(); continue; } @@ -950,7 +936,7 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator } _resourceDatabase.ReleaseResource(handle); - _temResources.Dequeue(); + _tempResources.Dequeue(); } } @@ -961,20 +947,15 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator return; } -#if DEBUG || GHOST_EDITOR - if (_temResources.Count > 0) - { - throw new InvalidOperationException($"ResourceAllocator is being disposed with {_temResources.Count} temp allocations still registered. Ensure all resources are released before disposing."); - } -#endif + Debug.Assert(_tempResources.Count == 0, "Temporary resources should be released before disposing the allocator."); - foreach (var handle in _temResources) + foreach (var handle in _tempResources) { _resourceDatabase.ReleaseResource(handle); } _d3d12MA.Dispose(); - _temResources.Dispose(); + _tempResources.Dispose(); _disposed = true; GC.SuppressFinalize(this); diff --git a/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs b/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs index 1660a02..816a22c 100644 --- a/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs +++ b/Ghost.Graphics/D3D12/D3D12ResourceDatabase.cs @@ -1,11 +1,11 @@ using Ghost.Core; using Ghost.Graphics.Core; +using Ghost.Graphics.D3D12.Utilities; using Ghost.Graphics.RHI; using Misaki.HighPerformance.Collections; using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; -using System.Diagnostics; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; @@ -42,7 +42,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase public readonly bool isExternal; public readonly bool Allocated => isExternal ? resourceUnion.resource.Get() != null : resourceUnion.allocation.Get() != null; - public readonly ID3D12Resource* ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource(); + public readonly SharedPtr ResourcePtr => isExternal ? resourceUnion.resource.Get() : resourceUnion.allocation.Get()->GetResource(); public ResourceRecord(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState state, ResourceViewGroup resourceDescriptor, ResourceDesc desc) { @@ -131,7 +131,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase resource = default!; } - public unsafe Handle ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, ResourceViewGroup viewGroup, string? name = null) + public unsafe Handle ImportExternalResource(ID3D12Resource* pResource, ResourceState initialState, ResourceViewGroup viewGroup, string name = "") { ObjectDisposedException.ThrowIf(_disposed, this); @@ -139,16 +139,14 @@ internal class D3D12ResourceDatabase : IResourceDatabase var handle = new Handle(id, generation); #if DEBUG || GHOST_EDITOR - if (name != null) - { - _resourceName[handle] = name; - } + pResource->SetName(name); + _resourceName[handle] = name; #endif return handle; } - public unsafe Handle AddResource(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null) + public unsafe Handle AddResource(D3D12MA_Allocation* allocation, uint cpuFenceValue, ResourceState initialState, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string name = "") { ObjectDisposedException.ThrowIf(_disposed, this); @@ -156,10 +154,8 @@ internal class D3D12ResourceDatabase : IResourceDatabase var handle = new Handle(id, generation); #if DEBUG || GHOST_EDITOR - if (name != null) - { - _resourceName[handle] = name; - } + allocation->SetName(name); + _resourceName[handle] = name; #endif return handle; @@ -184,7 +180,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase return ref info; } - public ref ResourceRecord GetResourceInfo(Handle handle, out bool exist) + public ref ResourceRecord GetResourceRecord(Handle handle, out bool exist) { ObjectDisposedException.ThrowIf(_disposed, this); return ref _resources.GetElementReferenceAt(handle.id, handle.generation, out exist); @@ -232,7 +228,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase { ObjectDisposedException.ThrowIf(_disposed, this); - ref var info = ref GetResourceInfo(handle, out var exist); + ref var info = ref GetResourceRecord(handle, out var exist); if (!exist || !info.Allocated) { return -1; @@ -254,7 +250,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase return null; } - public unsafe void ReleaseResource(Handle handle) + public void ReleaseResource(Handle handle) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -269,14 +265,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase return; } - var refCount = info.Release(_descriptorAllocator); + info.Release(_descriptorAllocator); + //Debug.Assert(info.Release(_descriptorAllocator) == 0); #if DEBUG || GHOST_EDITOR _resourceName.Remove(handle, out var name); - //if (refCount > 0) - //{ - // throw new GPUResourceLeakException(refCount, info.ResourcePtr, name ?? "Unknown Resource"); - //} - //Debug.Assert(refCount == 0, "Resource released with non-zero reference count."); #endif _resources.Remove(handle.id, handle.generation); @@ -405,34 +397,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase ReleaseResource(ref shader); } - public void AddShaderPass(ShaderPassKey passKey, ShaderPass pass) - { - ObjectDisposedException.ThrowIf(_disposed, this); - _shaderPasses.Add(passKey, pass); - } - - public ShaderPass GetShaderPass(ShaderPassKey passKey) - { - ObjectDisposedException.ThrowIf(_disposed, this); - - if (!_shaderPasses.TryGetValue(passKey, out var pass)) - { - throw new KeyNotFoundException($"Shader pass '{passKey}' not found."); - } - - return pass; - } - - public void RemoveShaderPass(ShaderPassKey passKey) - { - ObjectDisposedException.ThrowIf(_disposed, this); - - if (_shaderPasses.Remove(passKey, out var pass)) - { - ReleaseResource(ref pass); - } - } - public void Dispose() { static void ThrowMemoryLeakException(string resourceType, int count) diff --git a/Ghost.Graphics/D3D12/D3D12SwapChain.cs b/Ghost.Graphics/D3D12/D3D12SwapChain.cs index c458afd..570e530 100644 --- a/Ghost.Graphics/D3D12/D3D12SwapChain.cs +++ b/Ghost.Graphics/D3D12/D3D12SwapChain.cs @@ -7,6 +7,7 @@ using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; +using System.Diagnostics; using System.Runtime.CompilerServices; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -27,7 +28,7 @@ internal unsafe class D3D12SwapChain : ISwapChain private UniquePtr _swapChain; private UnsafeArray> _backBuffers; - private object? _compositionSurface; + private readonly object? _compositionSurface; private bool _disposed; @@ -48,20 +49,22 @@ internal unsafe class D3D12SwapChain : ISwapChain public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc) { + Debug.Assert(desc.BufferCount >= 2); + _resourceDatabase = resourceDatabase; _descriptorAllocator = descriptorAllocator; _renderDevice = device; - _backBuffers = new UnsafeArray>(D3D12PipelineResource.BACK_BUFFER_COUNT, Allocator.Persistent); + _backBuffers = new UnsafeArray>((int)desc.BufferCount, Allocator.Persistent); - Width = desc.width; - Height = desc.height; - BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT; + Width = desc.Width; + Height = desc.Height; + BufferCount = desc.BufferCount; CreateSwapChain(desc); CreateBackBuffers(); - _compositionSurface = desc.target.compositionSurface; + _compositionSurface = desc.Target.CompositionSurface; } ~D3D12SwapChain() @@ -73,9 +76,9 @@ internal unsafe class D3D12SwapChain : ISwapChain { var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1 { - Width = desc.width, - Height = desc.height, - Format = desc.format.ToDXGIFormat(), + Width = desc.Width, + Height = desc.Height, + Format = desc.Format.ToDXGIFormat(), SampleDesc = new DXGI_SAMPLE_DESC(1, 0), BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT, BufferCount = D3D12PipelineResource.BACK_BUFFER_COUNT, @@ -91,15 +94,15 @@ internal unsafe class D3D12SwapChain : ISwapChain var pFactory = _renderDevice.DXGIFactory.Get(); var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get(); - switch (desc.target.type) + switch (desc.Target.Type) { case SwapChainTargetType.Composition: ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain)); // Set the composition surface - if (desc.target.compositionSurface != null) + if (desc.Target.CompositionSurface != null) { - using var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.target.compositionSurface); + using var swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(desc.Target.CompositionSurface); swapChainPanelNative.SetSwapChain((IntPtr)pTempSwapChain); } break; @@ -112,7 +115,7 @@ internal unsafe class D3D12SwapChain : ISwapChain pFactory->CreateSwapChainForHwnd( (IUnknown*)pCommandQueue, - new HWND(desc.target.windowHandle.ToPointer()), + new HWND(desc.Target.WindowHandle.ToPointer()), &swapChainDesc, &swapChainFullscreenDesc, null, @@ -141,7 +144,12 @@ internal unsafe class D3D12SwapChain : ISwapChain var rtv = _descriptorAllocator.AllocateRTV(); _renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, _descriptorAllocator.GetCpuHandle(rtv)); - var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present, new ResourceViewGroup() { rtv = rtv }); + var view = ResourceViewGroup.Invalid with + { + rtv = rtv + }; + + var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, ResourceState.Present, view); _backBuffers[i] = handle.AsTexture(); } } diff --git a/Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs b/Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs index 31d8446..d32ed59 100644 --- a/Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs +++ b/Ghost.Graphics/D3D12/Utilities/D3D12DescriptorHeap.cs @@ -164,12 +164,17 @@ internal unsafe struct D3D12DescriptorHeap : IDisposable public void ReleaseDescriptors(int baseIndex, int count = 1) { + if (baseIndex == _INVALID_DESCRIPTOR_INDEX) + { + return; + } + if (count == 0) { return; } - if (baseIndex < _dynamicHeapStart) + if (baseIndex >= _dynamicHeapStart) { // Dynamic allocations are not released individually. return; diff --git a/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs b/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs index 21ff439..d75ea0a 100644 --- a/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs +++ b/Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs @@ -10,6 +10,19 @@ internal unsafe static class D3D12Utility { public static void SetName(ref this T obj, ReadOnlySpan name) where T : unmanaged, ID3D12Object.Interface + { + if (name.Length == 0) + { + return; + } + + fixed (char* pName = name) + { + obj.SetName(pName); + } + } + + public static void SetName(ref this D3D12MA_Allocation obj, ReadOnlySpan name) { fixed (char* pName = name) { diff --git a/Ghost.Graphics/Ghost.Graphics.csproj b/Ghost.Graphics/Ghost.Graphics.csproj index 0acf480..a122b0b 100644 --- a/Ghost.Graphics/Ghost.Graphics.csproj +++ b/Ghost.Graphics/Ghost.Graphics.csproj @@ -10,10 +10,12 @@ True + True True + True @@ -52,4 +54,10 @@ + + + ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net10.0\Misaki.HighPerformance.LowLevel.dll + + + diff --git a/Ghost.Graphics/RHI/Common.cs b/Ghost.Graphics/RHI/Common.cs index bb601bb..cbaea5c 100644 --- a/Ghost.Graphics/RHI/Common.cs +++ b/Ghost.Graphics/RHI/Common.cs @@ -154,7 +154,6 @@ public ref struct GraphicsPSODescriptor get; set; } - public ReadOnlySpan RtvFormats { get; set; @@ -267,7 +266,6 @@ public struct RectDesc { get; set; } - } public struct SubResourceData @@ -311,6 +309,24 @@ public struct PassDepthStencilDesc } +public struct BarrierDesc +{ + public Handle Resource + { + get; set; + } + + public ResourceState StateBefore + { + get; set; + } + + public ResourceState StateAfter + { + get; set; + } +} + public struct ResourceDesc { [StructLayout(LayoutKind.Explicit)] @@ -381,13 +397,13 @@ public struct ResourceDesc } /// -/// Render target description +/// Render Target description /// Supports either color OR depth rendering, not both /// public struct RenderTargetDesc { /// - /// Width of the render target + /// Width of the render Target /// public uint Width { @@ -395,7 +411,7 @@ public struct RenderTargetDesc } /// - /// Height of the render target + /// Height of the render Target /// public uint Height { @@ -403,7 +419,7 @@ public struct RenderTargetDesc } /// - /// Slice of the render target + /// Slice of the render Target /// public uint Slice { @@ -411,7 +427,7 @@ public struct RenderTargetDesc } /// - /// Type of render target + /// Type of render Target /// public RenderTargetType Type { @@ -419,7 +435,7 @@ public struct RenderTargetDesc } /// - /// Target texture format + /// Target texture Format /// public TextureFormat Format { @@ -435,7 +451,7 @@ public struct RenderTargetDesc } /// - /// Creation flags for the render target + /// Creation flags for the render Target /// public RenderTargetCreationFlags CreationFlags { @@ -459,7 +475,7 @@ public struct RenderTargetDesc } /// - /// Creates a color render target + /// Creates a color render Target /// public static RenderTargetDesc Color(uint width, uint height, uint slice = 1, TextureFormat format = TextureFormat.R8G8B8A8_UNorm, TextureDimension dimension = TextureDimension.Texture2D, @@ -481,7 +497,7 @@ public struct RenderTargetDesc } /// - /// Creates a depth render target + /// Creates a depth render Target /// public static RenderTargetDesc Depth(uint width, uint height, uint slice = 1, TextureFormat format = TextureFormat.D24_UNorm_S8_UInt, TextureDimension dimension = TextureDimension.Texture2D, @@ -554,7 +570,7 @@ public struct TextureDesc } /// - /// Texture format + /// Texture Format /// public TextureFormat Format { @@ -630,59 +646,82 @@ public struct SwapChainDesc /// /// Width of the swap chain /// - public uint width; + public uint Width + { + get; set; + } /// /// Height of the swap chain /// - public uint height; - - /// - /// Back buffer format - /// - public TextureFormat format; - - /// - /// Target for presentation (window handle or composition target) - /// - public SwapChainTarget target; - - public SwapChainDesc(uint width, uint height, SwapChainTarget target, TextureFormat format = TextureFormat.B8G8R8A8_UNorm, uint bufferCount = 2) + public uint Height { - this.width = width; - this.height = height; - this.format = format; - this.target = target; + get; set; + } + + + /// + /// Back buffer Format + /// + public TextureFormat Format + { + get; set; + } + + + /// + /// Target for presentation (window handle or composition Target) + /// + public SwapChainTarget Target + { + get; set; + } + + public uint BufferCount + { + get; set; } } /// -/// Swap chain target (window handle or composition surface) +/// Swap chain Target (window handle or composition surface) /// public struct SwapChainTarget { /// /// Target type /// - public SwapChainTargetType type; + public SwapChainTargetType Type + { + get; set; + } + /// /// Window handle for HWND targets /// - public nint windowHandle; + public nint WindowHandle + { + get; set; + } + /// /// Composition surface for UWP/WinUI targets /// - public object? compositionSurface; + public object? CompositionSurface + { + get; set; + } + public static SwapChainTarget FromWindowHandle(nint hwnd) { return new SwapChainTarget { - type = SwapChainTargetType.WindowHandle, - windowHandle = hwnd, - compositionSurface = null + Type = SwapChainTargetType.WindowHandle, + WindowHandle = hwnd, + CompositionSurface = null }; } @@ -690,9 +729,9 @@ public struct SwapChainTarget { return new SwapChainTarget { - type = SwapChainTargetType.Composition, - windowHandle = nint.Zero, - compositionSurface = surface + Type = SwapChainTargetType.Composition, + WindowHandle = 0, + CompositionSurface = surface }; } } diff --git a/Ghost.Graphics/RHI/ICommandBuffer.cs b/Ghost.Graphics/RHI/ICommandBuffer.cs index 359d225..c27e98f 100644 --- a/Ghost.Graphics/RHI/ICommandBuffer.cs +++ b/Ghost.Graphics/RHI/ICommandBuffer.cs @@ -53,21 +53,21 @@ public interface ICommandBuffer : IDisposable void SetScissorRect(RectDesc rect); /// - /// Sets the optional render targets and optional depth target for subsequent rendering operations. + /// Sets the optional render targets and optional depth Target for subsequent rendering operations. /// /// /// To specify no render targets, provide an empty span for . - /// Use for if no depth target is required. + /// Use for if no depth Target is required. /// /// A read-only span of handles to textures that will be used as render targets. /// The order of handles determines the order in which render targets are bound. - /// A handle to the texture to be used as the depth target. Specify a invalid handle if no depth target is required. + /// A handle to the texture to be used as the depth Target. Specify a invalid handle if no depth Target is required. void SetRenderTargets(ReadOnlySpan> renderTargets, Handle depthTarget); /// - /// Begins a render pass with the specified render target + /// Begins a render pass with the specified render Target /// - /// Render target descriptions + /// Render Target descriptions /// Depth stencil description /// Whether UAV writes are allowed during the render pass void BeginRenderPass(ReadOnlySpan rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false); @@ -78,12 +78,18 @@ public interface ICommandBuffer : IDisposable void EndRenderPass(); /// - /// Inserts a resource barrier for state transitions + /// Inserts multiple resource barriers for state transitions. /// - /// Resource to transition - /// Current resource state - /// Target resource state - void ResourceBarrier(Handle resource, ResourceState before, ResourceState after); + /// Resource barrier descriptions + void ResourceBarrier(ReadOnlySpan barrierDescs); + + /// + /// Inserts a resource barrier for state transitions. + /// + /// A handle to the GPU resource to transition. + /// The current state of the resource before the transition. + /// The desired state of the resource after the transition. + void ResourceBarrier(Handle resource, ResourceState stateBefore, ResourceState stateAfter); /// /// Sets the pipeline state object @@ -176,10 +182,10 @@ public interface ICommandBuffer : IDisposable /// /// The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle. /// The index of the first subresource in the texture to receive data. Must be less than the total number of subresources in the texture. - /// A reference to the structure containing the subresource data to upload. The data must match the format and layout expected by the texture. + /// A reference to the structure containing the subresource data to upload. The data must match the Format and layout expected by the texture. /// The number of subresources to upload, starting from . /// Must be greater than zero and not exceed the remaining subresources in the texture. - void UploadTexture(Handle texture, params ReadOnlySpan subresources); + void UploadTexture(Handle texture, ReadOnlySpan subresources); /// /// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer. diff --git a/Ghost.Graphics/RHI/IPipelineLibrary.cs b/Ghost.Graphics/RHI/IPipelineLibrary.cs index a9f5f89..62b720c 100644 --- a/Ghost.Graphics/RHI/IPipelineLibrary.cs +++ b/Ghost.Graphics/RHI/IPipelineLibrary.cs @@ -23,5 +23,4 @@ public interface IPipelineLibrary : IDisposable void InitializeLibrary(string? filePath); void SaveLibraryToDisk(string filePath); Result CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled); - Result GetCBufferInfo(ShaderPassKey passId); } diff --git a/Ghost.Graphics/RHI/IRenderer.cs b/Ghost.Graphics/RHI/IRenderer.cs index 8260242..f8b0975 100644 --- a/Ghost.Graphics/RHI/IRenderer.cs +++ b/Ghost.Graphics/RHI/IRenderer.cs @@ -15,9 +15,9 @@ public interface IRenderer : IDisposable } /// - /// Sets the render target for this renderer + /// Sets the render Target for this renderer /// - /// Render target to render into + /// Render Target to render into public void SetRenderTarget(Handle renderTarget); /// diff --git a/Ghost.Graphics/RHI/IResourceAllocator.cs b/Ghost.Graphics/RHI/IResourceAllocator.cs index ba4e645..4525025 100644 --- a/Ghost.Graphics/RHI/IResourceAllocator.cs +++ b/Ghost.Graphics/RHI/IResourceAllocator.cs @@ -15,9 +15,9 @@ public interface IResourceAllocator : IDisposable public Handle CreateTexture(ref readonly TextureDesc desc, bool tempResource = false); /// - /// Creates a render target for off-screen rendering + /// Creates a render Target for off-screen rendering /// - /// Render target description + /// Render Target description /// A new texture handle point to the resource public Handle CreateRenderTarget(ref readonly RenderTargetDesc desc, bool tempResource = false); diff --git a/Ghost.Graphics/RHI/IResourceDatabase.cs b/Ghost.Graphics/RHI/IResourceDatabase.cs index 639c346..fec83b6 100644 --- a/Ghost.Graphics/RHI/IResourceDatabase.cs +++ b/Ghost.Graphics/RHI/IResourceDatabase.cs @@ -11,6 +11,9 @@ public interface IResourceReleasable void ReleaseResource(IResourceDatabase database); } +// TODO: Consider adding methods for resource enumeration, statistics, and bulk operations. +// TODO: Consider adding async resource loading and streaming support. +// TODO: Mesh, Material, Shader management could be separated into their own interfaces for better modularity because they are not bound to specific graphics API. public interface IResourceDatabase : IDisposable { /* @@ -155,18 +158,4 @@ public interface IResourceDatabase : IDisposable /// /// The identifier of the shader to release. Must refer to a valid, previously created shader. void ReleaseShader(Identifier id); - - /// - /// Adds a shader pass to the collection using the specified identifier. - /// - /// The unique identifier for the shader pass. - /// The shader pass to add. Cannot be null. - void AddShaderPass(ShaderPassKey passKey, ShaderPass pass); - - /// - /// Retrieves the shader pass associated with the specified pass identifier. - /// - /// The unique identifier of the shader pass to retrieve. - /// The corresponding to the specified identifier, or null if no matching shader pass is found. - ShaderPass GetShaderPass(ShaderPassKey passKey); } diff --git a/Ghost.Graphics/RHI/RHIUtility.cs b/Ghost.Graphics/RHI/RHIUtility.cs index ca9ba9c..a829160 100644 --- a/Ghost.Graphics/RHI/RHIUtility.cs +++ b/Ghost.Graphics/RHI/RHIUtility.cs @@ -22,8 +22,8 @@ internal static class RHIUtility var packed = false; var planar = false; var bpe = 0; - - //switch (format) + + //switch (Format) //{ // case Format.BC1Typeless: // case Format.BC1Unorm: diff --git a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs index c6fb500..ab30860 100644 --- a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs +++ b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs @@ -5,23 +5,33 @@ using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Ghost.Graphics.Utilities; using Ghost.SDL.Compiler; -using Misaki.HighPerformance.Image; using Misaki.HighPerformance.Mathematics; -using Misaki.HighPerformance.Utilities; -using TerraFX.Interop.Windows; +using System.Runtime.InteropServices; namespace Ghost.Graphics.RenderPasses; /// /// Simplified bindless mesh render pass using high-level bindless APIs with fully bindless vertex/index buffer access /// -internal unsafe class MeshRenderPass : IRenderPass +internal class MeshRenderPass : IRenderPass { + [StructLayout(LayoutKind.Sequential)] + private struct ShaderProperties_MyShader_Standard + { + public float4 color; + public uint texture1; + public uint texture2; + public uint texture3; + public uint texture4; + } + private Handle _mesh; private Identifier _shader; private Handle _material; private Handle[]? _textures; + private GraphicsCompiledResult[]? _compileResults; + // Texture file paths for this demo private readonly string[] _textureFiles = [ "C:/Users/Misaki/Downloads/Im/Icon.png", @@ -34,9 +44,11 @@ internal unsafe class MeshRenderPass : IRenderPass { var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow(); - foreach (var pass in shaderDescriptor.passes) + _compileResults = new GraphicsCompiledResult[shaderDescriptor.passes.Count]; + for (var i = 0; i < shaderDescriptor.passes.Count; i++) { - var compileResult = ctx.ShaderCompiler.CompilePass(pass); + var pass = shaderDescriptor.passes[i]; + var compileResult = ctx.ShaderCompiler.CompilePass(pass, shaderDescriptor.generatedCodePath); if (compileResult.IsFailure || pass is not FullPassDescriptor fullPass) { continue; @@ -55,18 +67,30 @@ internal unsafe class MeshRenderPass : IRenderPass DsvFormat = TextureFormat.Unknown, }; - ctx.PipelineLibrary.CompilePSO(in psoDes, in compileResult.GetValueRef()).GetValueOrThrow(); + _compileResults[i] = compileResult.Value; + ctx.PipelineLibrary.CompilePSO(in psoDes, in _compileResults[i]).GetValueOrThrow(); } MeshBuilder.CreateCube(0.75f, default, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, out var vertices, out var indices); - _mesh = ctx.CreateMesh(vertices, indices); + _mesh = ctx.CreateMesh(vertices, indices, true); ctx.UpdateObjectData(_mesh, float4x4.identity); - ctx.UploadMesh(_mesh, true); _shader = ctx.ResourceAllocator.CreateGraphicsShader(shaderDescriptor); _material = ctx.ResourceAllocator.CreateMaterial(_shader); + ref var matRef = ref ctx.ResourceDatabase.GetMaterialReference(_material); + var matProps = new ShaderProperties_MyShader_Standard + { + color = new float4(1.0f, 1.0f, 1.0f, 1.0f), + texture1 = 0, + texture2 = 1, + texture3 = 2, + texture4 = 3, + }; + + matRef.SetPropertyCache(in matProps); + //_textures = new Handle[_textureFiles.Length]; //for (var i = 0; i < _textureFiles.Length; i++) //{ @@ -107,5 +131,13 @@ internal unsafe class MeshRenderPass : IRenderPass resourceDatabase.ReleaseResource(texture.AsResource()); } } + + if (_compileResults != null) + { + for (var i = 0; i < _compileResults.Length; i++) + { + _compileResults[i].Dispose(); + } + } } } diff --git a/Ghost.Graphics/RenderPasses/ShaderCode.hlsl b/Ghost.Graphics/RenderPasses/ShaderCode.hlsl index 359caf9..2d4af10 100644 --- a/Ghost.Graphics/RenderPasses/ShaderCode.hlsl +++ b/Ghost.Graphics/RenderPasses/ShaderCode.hlsl @@ -1,15 +1,7 @@ #include GENERATED_CODE_PATH #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Properties.hlsl" - -struct Vertex -{ - float4 position; - float4 normal; - float4 tangent; - float4 color; - float4 uv; -}; +#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl" struct PixelInput { @@ -26,24 +18,8 @@ void MSMain( out vertices PixelInput outVerts[3], out indices uint3 outTris[1]) { -#if 0 - // Fetch bindless buffers - ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[g_PerObjectData.vertexBuffer]; - ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[g_PerObjectData.indexBuffer]; - - // Compute the triangle’s vertex indices uint vertexId = groupThreadID.x; - uint indexOffset = (groupID.x * 3 + vertexId) * 4; // uint32 index - uint vertexIndex = indexBuffer.Load(indexOffset); - - // Load vertex attributes - uint vertexOffset = vertexIndex * 80; // 80 bytes per vertex - Vertex v; - v.position = asfloat(vertexBuffer.Load4(vertexOffset + 0)); - v.normal = asfloat(vertexBuffer.Load4(vertexOffset + 16)); - v.tangent = asfloat(vertexBuffer.Load4(vertexOffset + 32)); - v.color = asfloat(vertexBuffer.Load4(vertexOffset + 48)); - v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 64)); + Vertex v = LoadVertexData(vertexId, groupID.x, g_PerObjectData.vertexBuffer, g_PerObjectData.indexBuffer); SetMeshOutputCounts(3, 1); //v.position = mul(g_PerViewData.cameraMatrix, mul(g_PerObjectData.localToWorld, v.position)); @@ -58,50 +34,16 @@ void MSMain( { outTris[0] = uint3(0, 1, 2); } -#else - // 1. Tell the hardware how much data to expect - SetMeshOutputCounts(3, 1); - - // 2. Hardcoded Clip Space Positions (X, Y, Z, W) - // Visible range: X[-1, 1], Y[-1, 1], Z[0, 1] - // W must be 1.0 - float4 positions[3] = - { - float4(0.0f, 0.5f, 0.5f, 1.0f), // Top - float4(0.5f, -0.5f, 0.5f, 1.0f), // Bottom Right - float4(-0.5f, -0.5f, 0.5f, 1.0f) // Bottom Left - }; - - float4 colors[3] = - { - float4(g_PerObjectData.vertexBuffer, 0.0f, 0.0f, 1.0f), // Red - float4(0.0f, g_PerObjectData.indexBuffer, 0.0f, 1.0f), // Green - float4(0.0f, 0.0f, 0.0f, 1.0f) // Blue - }; - - uint gtid = groupThreadID.x; - - // 3. Write Vertex Data (Parallel) - outVerts[gtid].position = positions[gtid]; - outVerts[gtid].color = colors[gtid]; - - // 4. Write Index Data (Only 1st thread needs to do this) - if (gtid == 0) - { - // Clockwise winding (Standard for DX12) - outTris[0] = uint3(0, 1, 2); - } -#endif } float4 PSMain(PixelInput input) : SV_TARGET { - //float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture1, 0, input.uv.xy); - //float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture2, 0, input.uv.xy); - //float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture3, 0, input.uv.xy); - //float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(g_PerMaterialData.texture4, 0, input.uv.xy); + //float4 color1 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture1, 0, input.uv.xy); + //float4 color2 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture2, 0, input.uv.xy); + //float4 color3 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture3, 0, input.uv.xy); + //float4 color4 = SAMPLE_TEXTURE2D(g_PerMaterialData.texture4, 0, input.uv.xy); //float4 blendedColor = (color1 + color2 + color3 + color4) * 0.25f; - return g_PerMaterialData.color + input.color;; + return g_PerMaterialData.color + input.color; //return input.color; } diff --git a/Ghost.Graphics/RenderSystem.cs b/Ghost.Graphics/RenderSystem.cs index 94357ab..18ddaad 100644 --- a/Ghost.Graphics/RenderSystem.cs +++ b/Ghost.Graphics/RenderSystem.cs @@ -48,11 +48,6 @@ public interface IFenceSynchronizer public interface IRenderSystem : IFenceSynchronizer, IDisposable { - RenderingConfig Config - { - get; - } - IGraphicsEngine GraphicsEngine { get; @@ -80,8 +75,8 @@ internal class RenderSystem : IRenderSystem public FrameResource() { - cpuReadyEvent = new(false); - gpuReadyEvent = new(true); + cpuReadyEvent = new AutoResetEvent(false); + gpuReadyEvent = new AutoResetEvent(true); } public void Dispose() @@ -105,7 +100,6 @@ internal class RenderSystem : IRenderSystem private bool _isRunning; private bool _disposed; - public RenderingConfig Config => _config; public IGraphicsEngine GraphicsEngine => _graphicsEngine; public bool IsRunning => _isRunning; @@ -123,16 +117,16 @@ internal class RenderSystem : IRenderSystem _ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.") }; - _shutdownEvent = new(false); + _shutdownEvent = new AutoResetEvent(false); // Create frame resources for synchronization _frameResources = new FrameResource[config.FrameBufferCount]; for (var i = 0; i < config.FrameBufferCount; i++) { - _frameResources[i] = new(); + _frameResources[i] = new FrameResource(); } - _renderThread = new(RenderLoop) + _renderThread = new Thread(RenderLoop) { IsBackground = true, Name = "Graphics Render Thread", diff --git a/Ghost.Graphics/Utilities/DxcShaderCompiler.cs b/Ghost.Graphics/Utilities/DxcShaderCompiler.cs index 6638569..2369f8b 100644 --- a/Ghost.Graphics/Utilities/DxcShaderCompiler.cs +++ b/Ghost.Graphics/Utilities/DxcShaderCompiler.cs @@ -138,7 +138,7 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler _compiler.Attach(pCompiler); _utils.Attach(pUtils); - _compiledResults = new(); + _compiledResults = new Dictionary(); } ~DxcShaderCompiler() @@ -153,10 +153,10 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler try { - // Create DXC _utils.Get() to parse pReflection data + // Create DXC _utils.Get() to parse reflection data var dxcuID = CLSID.CLSID_DxcUtils; - // Create pReflection interface from blob + // Create reflection interface from blob var reflectionBuffer = new DxcBuffer { Ptr = pDxcReflectionBlob->GetBufferPointer(), @@ -242,137 +242,109 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler } } - public Result Compile(ref readonly CompilerConfig config, Allocator allocator) + public Result Compile(ref readonly CompilerConfig config, Allocator allocator) { ObjectDisposedException.ThrowIf(_disposed, this); - IDxcIncludeHandler* pIncludeHandler = default; - IDxcBlobEncoding* pSourceBlob = default; + using ComPtr includeHandler = default; + using ComPtr sourceBlob = default; + + // Create DXC _compiler.Get() and _utils.Get() + var dxccID = CLSID.CLSID_DxcCompiler; + var dxcuID = CLSID.CLSID_DxcUtils; + + ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf())); + + // Create source blob + fixed (char* pPath = config.shaderPath) + { + if (_utils.Get()->LoadFile(pPath, null, sourceBlob.GetAddressOf()).FAILED) + { + return Result.Failure($"Failed to load shader file: {config.shaderPath}"); + } + } + + var argsArray = GetCompilerArguments(in config); + var argPtrs = stackalloc char*[argsArray.Count]; + for (var i = 0; i < argsArray.Count; i++) + { + argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]); + } + + using ComPtr result = default; try { - // Create DXC _compiler.Get() and _utils.Get() - var dxccID = CLSID.CLSID_DxcCompiler; - var dxcuID = CLSID.CLSID_DxcUtils; - - - ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(&pIncludeHandler)); - - // Create source blob - fixed (char* pPath = config.shaderPath) + // Compile shader + var buffer = new DxcBuffer { - if (_utils.Get()->LoadFile(pPath, null, &pSourceBlob).FAILED) + Ptr = sourceBlob.Get()->GetBufferPointer(), + Size = sourceBlob.Get()->GetBufferSize(), + Encoding = DXC_CP_UTF8 + }; + + var (iid, ppv) = Win32Utility.IID_PPV_ARGS(&result); + ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler, iid, ppv)); + + // Check compilation result + HRESULT hrStatus; + result.Get()->GetStatus(&hrStatus); + if (hrStatus.FAILED) + { + // Get error messages + IDxcBlobEncoding* pErrorBlob = default; + result.Get()->GetErrorBuffer(&pErrorBlob); + + if (pErrorBlob != null) { - return Result.Failure($"Failed to load shader file: {config.shaderPath}"); + var errorMessage = Marshal.PtrToStringUTF8((IntPtr)pErrorBlob->GetBufferPointer()); + pErrorBlob->Release(); + + return Result.Failure($"DXC shader compilation failed:\n{errorMessage}"); + } + else + { + return Result.Failure("DXC shader compilation failed with unknown error."); } } - var argsArray = GetCompilerArguments(in config); - var argPtrs = stackalloc char*[argsArray.Count]; - for (var i = 0; i < argsArray.Count; i++) + // Get compiled bytecode + using ComPtr bytecodeBlob = default; + ThrowIfFailed(result.Get()->GetResult(bytecodeBlob.GetAddressOf())); + + ShaderReflectionData reflectionData = default; + if (config.options.HasFlag(CompilerOption.KeepReflections)) { - argPtrs[i] = (char*)Marshal.StringToHGlobalUni(argsArray[i]); + using ComPtr reflection = default; + (iid, ppv) = Win32Utility.IID_PPV_ARGS(&reflection); + + if (result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, iid, ppv, null).SUCCEEDED) + { + reflectionData = PerformDXCReflection(reflection).GetValueOrDefault(); + } } - IDxcResult* pResult = default; + var bytecodeSize = bytecodeBlob.Get()->GetBufferSize(); + var bytecode = new UnsafeArray((int)bytecodeSize, allocator); - try + NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize); + + return new ShaderCompileResult { - // Compile shader - var buffer = new DxcBuffer - { - Ptr = pSourceBlob->GetBufferPointer(), - Size = pSourceBlob->GetBufferSize(), - Encoding = DXC_CP_UTF8 - }; - - ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult)); - - // Check compilation pResult - HRESULT hrStatus; - pResult->GetStatus(&hrStatus); - if (hrStatus.FAILED) - { - // Get error messages - IDxcBlobEncoding* pErrorBlob = default; - pResult->GetErrorBuffer(&pErrorBlob); - - if (pErrorBlob != null) - { - var errorMessage = Marshal.PtrToStringUTF8((IntPtr)pErrorBlob->GetBufferPointer()); - pErrorBlob->Release(); - - return Result.Failure($"DXC shader compilation failed:\n{errorMessage}"); - } - else - { - return Result.Failure("DXC shader compilation failed with unknown error."); - } - } - - // Get compiled bytecode - IDxcBlob* pBytecodeBlob = default; - try - { - ThrowIfFailed(pResult->GetResult(&pBytecodeBlob)); - - ShaderReflectionData reflection = default; - if (config.options.HasFlag(CompilerOption.KeepReflections)) - { - IDxcBlob* pReflection = default; - if ((pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof(), (void**)&pReflection, null).SUCCEEDED)) - { - reflection = PerformDXCReflection(pReflection).GetValueOrDefault(); - } - - if (pReflection != null) - { - pReflection->Release(); - } - } - - var bytecodeSize = pBytecodeBlob->GetBufferSize(); - var bytecode = new UnsafeArray((int)bytecodeSize, allocator); - - NativeMemory.Copy(pBytecodeBlob->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize); - - return new CompileResult - { - bytecode = bytecode, - reflectionData = reflection, - }; - } - finally - { - if (pBytecodeBlob != null) - { - pBytecodeBlob->Release(); - } - } - } - finally - { - for (var i = 0; i < argsArray.Count; i++) - { - Marshal.FreeHGlobal((nint)argPtrs[i]); - } - - if (pResult != null) - { - pResult->Release(); - } - } + bytecode = bytecode, + reflectionData = reflectionData, + }; } finally { - if (pIncludeHandler != null) + for (var i = 0; i < argsArray.Count; i++) { - pIncludeHandler->Release(); + Marshal.FreeHGlobal((nint)argPtrs[i]); } } } - public Result CompilePass(IPassDescriptor descriptor) + public Result CompilePass(IPassDescriptor descriptor, string? generatedCodePath) { ObjectDisposedException.ThrowIf(_disposed, this); @@ -381,14 +353,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler return Result.Failure("FullPassDescriptor expected."); } - CompileResult tsResult = default; + ShaderCompileResult tsResult = default; var tsEntry = fullDescriptor.taskShader; if (tsEntry.IsCreated) { var config = new CompilerConfig { defines = fullDescriptor.defines.AsSpan(), - include = fullDescriptor.generatedCodePath, + include = generatedCodePath, shaderPath = tsEntry.shader, entryPoint = tsEntry.entry, stage = ShaderStage.TaskShader, @@ -406,14 +378,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler tsResult = result.Value; } - CompileResult msResult; + ShaderCompileResult msResult; var msEntry = fullDescriptor.meshShader; if (msEntry.IsCreated) { var config = new CompilerConfig { defines = fullDescriptor.defines.AsSpan(), - include = fullDescriptor.generatedCodePath, + include = generatedCodePath, shaderPath = msEntry.shader, entryPoint = msEntry.entry, stage = ShaderStage.MeshShader, @@ -435,14 +407,14 @@ internal sealed unsafe partial class DxcShaderCompiler : IShaderCompiler return Result.Failure("Mesh shader expected."); } - CompileResult psResult; + ShaderCompileResult psResult; var psEntry = fullDescriptor.pixelShader; if (psEntry.IsCreated) { var config = new CompilerConfig { defines = fullDescriptor.defines.AsSpan(), - include = fullDescriptor.generatedCodePath, + include = generatedCodePath, shaderPath = psEntry.shader, entryPoint = psEntry.entry, stage = ShaderStage.PixelShader, diff --git a/Ghost.Graphics/Utilities/MeshBuilder.cs b/Ghost.Graphics/Utilities/MeshBuilder.cs index 9ba4541..1c22a7f 100644 --- a/Ghost.Graphics/Utilities/MeshBuilder.cs +++ b/Ghost.Graphics/Utilities/MeshBuilder.cs @@ -231,7 +231,7 @@ public unsafe static class MeshBuilder public static void ComputeTangents(UnsafeList vertices, UnsafeList indices) { using var scope = AllocationManager.CreateStackScope(); - var bitangents = new UnsafeArray(vertices.Count, Allocator.Stack); + using var bitangents = new UnsafeArray(vertices.Count, Allocator.Stack); for (var i = 0; i < indices.Count; i += 3) { diff --git a/Ghost.Graphics/test.gshader b/Ghost.Graphics/test.gshader index 3070a85..d9dffdf 100644 --- a/Ghost.Graphics/test.gshader +++ b/Ghost.Graphics/test.gshader @@ -3,10 +3,10 @@ shader "MyShader/Standard" properties { float4 color = float4(1, 1, 1, 1); - tex2d_b texture1 = tex2d_b(black); - tex2d_b texture2 = tex2d_b(white); - tex2d_b texture3 = tex2d_b(grey); - tex2d_b texture4 = tex2d_b(normal); + tex2d texture1 = tex2d(black); + tex2d texture2 = tex2d(white); + tex2d texture3 = tex2d(grey); + tex2d texture4 = tex2d(normal); } pass "Forward" @@ -23,4 +23,4 @@ shader "MyShader/Standard" ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain"); ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain"); } -} \ No newline at end of file +} diff --git a/Ghost.Shader.Test/Program.cs b/Ghost.Shader.Test/Program.cs index 5ab5816..3829415 100644 --- a/Ghost.Shader.Test/Program.cs +++ b/Ghost.Shader.Test/Program.cs @@ -30,11 +30,7 @@ if (model == null) } var descriptor = SDLCompiler.ResolveShader(model); - -foreach (var pass in descriptor.passes) -{ - SDLCompiler.GeneratePass(pass, "C:/Users/Misaki/Downloads/Archive"); -} +SDLCompiler.GenerateShaderCode(descriptor, "C:/Users/Misaki/Downloads/Archive"); Console.WriteLine("Shader compiled successfully:"); diff --git a/Ghost.Shader/BuiltIn/Common.hlsl b/Ghost.Shader/BuiltIn/Common.hlsl index 724f68a..e9bc3c3 100644 --- a/Ghost.Shader/BuiltIn/Common.hlsl +++ b/Ghost.Shader/BuiltIn/Common.hlsl @@ -1,97 +1,95 @@ -#ifndef COMMON_HLSL -#define COMMON_HLSL - -#undef USE_TRADITIONAL_BINDLESS // Just for testing, this should be handled by engine feature level. +#ifndef BUILTIN_COMMON_HLSL +#define BUILTIN_COMMON_HLSL +struct Vertex +{ + float4 position; + float4 normal; + float4 tangent; + float4 uv; + float4 color; +}; // Resource descriptor heap definitions -#if defined(USE_TRADITIONAL_BINDLESS) -#define GLOBAL_TEXTURE2D_HEAP_SIZE 32768 -#define GLOBAL_TEXTURE3D_HEAP_SIZE 32 -#define GLOBAL_TEXTURECUBE_HEAP_SIZE 32 -#define GLOBAL_TEXTURE2D_ARRAY_HEAP_SIZE 256 -#define GLOBAL_TEXTURECUBE_ARRAY_HEAP_SIZE 32 -#define GLOBAL_SAMPLER_HEAP_SIZE 32 - -#define GLOBAL_TEXTURE2D_HEAP GlobalTexture2DHeap -#define GLOBAL_TEXTURE3D_HEAP GlobalTexture3DHeap -#define GLOBAL_TEXTURECUBE_HEAP GlobalTextureCubeHeap -#define GLOBAL_TEXTURE2D_ARRAY_HEAP GlobalTexture2DArrayHeap -#define GLOBAL_TEXTURECUBE_ARRAY_HEAP GlobalTextureCubeArrayHeap -#define GLOBAL_SAMPLER_HEAP GlobalSamplerHeap -#else #define GLOBAL_TEXTURE2D_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURE3D_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURECUBE_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURE2D_ARRAY_HEAP ResourceDescriptorHeap #define GLOBAL_TEXTURECUBE_ARRAY_HEAP ResourceDescriptorHeap +#define GLOBAL_BUFFER_HEAP ResourceDescriptorHeap #define GLOBAL_SAMPLER_HEAP SamplerDescriptorHeap -#endif - // Bindless resource type definitions -#define TEXTURE2D_BINDLESS uint -#define TEXTURE3D_BINDLESS uint -#define TEXTURECUBE_BINDLESS uint -#define TEXTURE2D_ARRAY_BINDLESS uint -#define TEXTURECUBE_ARRAY_BINDLESS uint +#define TEXTURE2D uint +#define TEXTURE3D uint +#define TEXTURECUBE uint +#define TEXTURE2D_ARRAY uint +#define TEXTURECUBE_ARRAY uint -#define SAMPLER_BINDLESS uint +#define SAMPLER uint -#define STRUCT_BUFFER_BINDLESS uint -#define BYTE_ADDRESS_BUFFER_BINDLESS uint - -#define TEXTURE2D Texture2D -#define TEXTURE3D Texture3D -#define TEXTURECUBE TextureCube -#define TEXTURE2D_ARRAY Texture2DArray -#define TEXTURECUBE_ARRAY TextureCubeArray - -#define SAMPLER SamplerState - -#define STRUCT_BUFFER(type) StructuredBuffer -#define BYTE_ADDRESS_BUFFER ByteAddressBuffer +#define STRUCT_BUFFER uint +#define BYTE_ADDRESS_BUFFER uint // Texture and sampler access macros -#define GET_TEXTURE2D_BINDLESS(id) GLOBAL_TEXTURE2D_HEAP[id] -#define GET_TEXTURE2D_ARRAY_BINDLESS(id) GLOBAL_TEXTURE2D_ARRAY_HEAP[id] -#define GET_TEXTURE3D_BINDLESS(id) GLOBAL_TEXTURE3D_HEAP[id] -#define GET_TEXTURECUBE_BINDLESS(id) GLOBAL_TEXTURECUBE_HEAP[id] -#define GET_TEXTURECUBE_ARRAY_BINDLESS(id) GLOBAL_TEXTURECUBE_ARRAY_HEAP[id] -#define GET_SAMPLER_BINDLESS(id) GLOBAL_SAMPLER_HEAP[id] - - -// Texture sampling macros - -#define SAMPLE_TEXTURE2D(tex, samp, uv) tex.Sample(samp, uv) -#define SAMPLE_TEXTURE2D_LEVEL(tex, samp, uv, level) tex.SampleLevel(samp, uv, level) -#define SAMPLE_TEXTURE2D_BINDLESS(texId, sampId, uv) SampleTexture2DBindless(texId, sampId, uv) -#define SAMPLE_TEXTURE2D_LEVEL_BINDLESS(texId, sampId, uv, level) SampleTexture2DLevelBindless(texId, sampId, uv, level) - -#define SAMPLE_TEXTURE2D_ARRAY(tex, samp, uv, index) tex.Sample(samp, uv, index) -#define SAMPLE_TEXTURE2D_ARRAY_BINDLESS(texId, sampId, uv, index) GET_TEXTURE2D_ARRAY_BINDLESS(texId).Sample(GET_SAMPLER_BINDLESS(sampId), uv, index) - -static inline float4 SampleTexture2DBindless(uint texId, uint sampId, float2 uv) -{ - Texture2D tex = GET_TEXTURE2D_BINDLESS(texId); - SamplerState samp = GET_SAMPLER_BINDLESS(sampId); - return tex.Sample(samp, uv); -} - -static inline float4 SampleTexture2DLevelBindless(uint texId, uint sampId, float2 uv, float level) -{ - Texture2D tex = GET_TEXTURE2D_BINDLESS(texId); - SamplerState samp = GET_SAMPLER_BINDLESS(sampId); - return tex.SampleLevel(samp, uv, level); -} - - +#define GET_TEXTURE2D(id) GLOBAL_TEXTURE2D_HEAP[id] +#define GET_TEXTURE2D_ARRAY(id) GLOBAL_TEXTURE2D_ARRAY_HEAP[id] +#define GET_TEXTURE3D(id) GLOBAL_TEXTURE3D_HEAP[id] +#define GET_TEXTURECUBE(id) GLOBAL_TEXTURECUBE_HEAP[id] +#define GET_TEXTURECUBE_ARRAY(id) GLOBAL_TEXTURECUBE_ARRAY_HEAP[id] +#define GET_BUFFER(id) GLOBAL_BUFFER_HEAP[id] +#define GET_SAMPLER(id) GLOBAL_SAMPLER_HEAP[id] #define MESH_SHADER_THREADS(x) [NumThreads(x, 1, 1)] -#endif // COMMON_HLSL + +inline float4 SampleTexture2D(uint texId, uint sampId, float2 uv) +{ + Texture2D tex = GET_TEXTURE2D(texId); + SamplerState samp = GET_SAMPLER(sampId); + return tex.Sample(samp, uv); +} + +inline float4 SampleTexture2DLevel(uint texId, uint sampId, float2 uv, float level) +{ + Texture2D tex = GET_TEXTURE2D(texId); + SamplerState samp = GET_SAMPLER(sampId); + return tex.SampleLevel(samp, uv, level); +} + +inline float4 SampleTextureArray(uint texId, uint sampId, float3 uv) +{ + Texture2DArray tex = GET_TEXTURE2D_ARRAY(texId); + SamplerState samp = GET_SAMPLER(sampId); + return tex.Sample(samp, uvw.xyz); +} + + +inline Vertex LoadVertexData(uint vertexID, uint groupID, BYTE_ADDRESS_BUFFER vertexBuffer, BYTE_ADDRESS_BUFFER indexBuffer) +{ + // Fetch bindless buffers + ByteAddressBuffer vertexBuffer = GET_BUFFER(vertexBuffer); + ByteAddressBuffer indexBuffer = GET_BUFFER(indexBuffer); + + // Compute the triangle’s vertex indices + uint indexOffset = (groupID * 3 + vertexID) * 4; // uint32 index + uint vertexIndex = indexBuffer.Load(indexOffset); + + // Load vertex attributes + uint vertexOffset = vertexIndex * 80; // 80 bytes per vertex + Vertex v; + v.position = asfloat(vertexBuffer.Load4(vertexOffset + 0)); + v.normal = asfloat(vertexBuffer.Load4(vertexOffset + 16)); + v.tangent = asfloat(vertexBuffer.Load4(vertexOffset + 32)); + v.uv = asfloat(vertexBuffer.Load4(vertexOffset + 48)); + v.color = asfloat(vertexBuffer.Load4(vertexOffset + 64)); + + return v; +} + +#endif // BUILTIN_COMMON_HLSL diff --git a/Ghost.Shader/BuiltIn/Properties.hlsl b/Ghost.Shader/BuiltIn/Properties.hlsl index 81dc13b..e0e22df 100644 --- a/Ghost.Shader/BuiltIn/Properties.hlsl +++ b/Ghost.Shader/BuiltIn/Properties.hlsl @@ -1,5 +1,5 @@ -#ifndef PROPERTIES_HLSL -#define PROPERTIES_HLSL +#ifndef BUILTIN_PROPERTIES_HLSL +#define BUILTIN_PROPERTIES_HLSL #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl" @@ -48,4 +48,4 @@ TextureCubeArray GlobalTextureCubeArrayHeap[GLOBAL_TEXTURECUBE_ARRAY_HEAP_SIZE] SamplerState GlobalSamplerHeap[GLOBAL_SAMPLER_HEAP_SIZE] : register(s0); #endif -#endif \ No newline at end of file +#endif // BUILTIN_PROPERTIES_HLSL \ No newline at end of file diff --git a/Ghost.Shader/Compiler/Parser/IncludesBlock.cs b/Ghost.Shader/Compiler/Parser/IncludesBlock.cs deleted file mode 100644 index 57f99b3..0000000 --- a/Ghost.Shader/Compiler/Parser/IncludesBlock.cs +++ /dev/null @@ -1,59 +0,0 @@ -namespace Ghost.SDL.Compiler.Parser; - -internal class IncludesBlock : IBlockParser, List> -{ - public static bool ShouldEnter(Token token) - { - return token.Match(TokenType.Keyword, TokenLexicon.KnownKeywords.INCLUDES); - } - - public static List Parse(TokenStreamSlice stream) - { - stream.Expect(TokenType.Keyword); - stream.Expect(TokenType.LBrace); - - var includes = new List(); - - var bodyStream = stream.Slice(stream.Remaining - 1); - while (bodyStream.HasMore) - { - var includeToken = bodyStream.Expect(TokenType.StringLiteral); - includes.Add(includeToken); - bodyStream.Expect(TokenType.Semicolon); - } - - stream.Expect(TokenType.RBrace); - - return includes; - } - - public static List? SemanticAnalysis(List? syntax, List errors) - { - if (syntax == null || syntax.Count == 0) - { - return null; - } - - var includes = new List(syntax.Count); - foreach (var includeToken in syntax) - { - var path = includeToken.lexeme; - if (File.Exists(path)) - { - includes.Add(path); - } - else - { - errors.Add(new SDLError - { - message = $"Included file '{path}' not found.", - line = includeToken.line, - column = includeToken.column - }); - continue; - } - } - - return includes; - } -} diff --git a/Ghost.Shader/Compiler/Parser/PassBlock.cs b/Ghost.Shader/Compiler/Parser/PassBlock.cs index c890b91..bfbc62b 100644 --- a/Ghost.Shader/Compiler/Parser/PassBlock.cs +++ b/Ghost.Shader/Compiler/Parser/PassBlock.cs @@ -27,10 +27,6 @@ internal class PassBlock : IBlockParser { pass.defines = DefinesBlock.Parse(bodyStream.SliceNextBlock()); } - else if (IncludesBlock.ShouldEnter(nextToken)) - { - pass.includes = IncludesBlock.Parse(bodyStream.SliceNextBlock()); - } else if (KeywordsBlock.ShouldEnter(nextToken)) { pass.keywords = KeywordsBlock.Parse(bodyStream.SliceNextBlock()); @@ -39,10 +35,6 @@ internal class PassBlock : IBlockParser { pass.localPipeline = PipelineBlock.Parse(bodyStream.SliceNextBlock()); } - else if (PropertiesBlock.ShouldEnter(nextToken)) - { - pass.localProperties = PropertiesBlock.Parse(bodyStream.SliceNextBlock()); - } else if (nextToken.Match(TokenType.Identifier)) { var func = ParseUtility.ParseFunction(ref bodyStream, TokenType.StringLiteral); @@ -72,23 +64,10 @@ internal class PassBlock : IBlockParser { name = syntax.name.lexeme, defines = DefinesBlock.SemanticAnalysis(syntax.defines, errors), - includes = IncludesBlock.SemanticAnalysis(syntax.includes, errors), keywords = KeywordsBlock.SemanticAnalysis(syntax.keywords, errors), - localProperties = PropertiesBlock.SemanticAnalysis(syntax.localProperties, errors), localPipeline = PipelineBlock.SemanticAnalysis(syntax.localPipeline, errors), }; - if (semantic.localProperties != null - && semantic.localProperties.Any(p => p.scope == PropertyScope.Global)) - { - errors.Add(new SDLError - { - message = "Global properties cannot be declared inside a pass. Move them to the shader properties block.", - line = syntax.name.line, - column = syntax.name.column - }); - } - if (syntax.functionCalls != null) { foreach (var func in syntax.functionCalls) diff --git a/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs b/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs index f81c222..1ed2cb7 100644 --- a/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs +++ b/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs @@ -73,9 +73,9 @@ internal class PropertiesBlock : IBlockParser ParseTextureDefault(syntax[0], errors)), - [ShaderPropertyType.Texture3DBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), - [ShaderPropertyType.TextureCubeBindless] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), + [ShaderPropertyType.Texture2D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), + [ShaderPropertyType.Texture3D] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), + [ShaderPropertyType.TextureCube] = new(1, TokenType.Identifier, (syntax, errors) => ParseTextureDefault(syntax[0], errors)), }; private static float ParseFloatValue(Token token, List errors) @@ -166,6 +166,7 @@ internal class PropertiesBlock : IBlockParser ShaderPropertyType.Float2, TokenLexicon.KnownTypes.FLOAT3 => ShaderPropertyType.Float3, TokenLexicon.KnownTypes.FLOAT4 => ShaderPropertyType.Float4, + TokenLexicon.KnownTypes.FLOAT4X4 => ShaderPropertyType.Float4x4, TokenLexicon.KnownTypes.INT => ShaderPropertyType.Int, TokenLexicon.KnownTypes.INT2 => ShaderPropertyType.Int2, TokenLexicon.KnownTypes.INT3 => ShaderPropertyType.Int3, @@ -178,9 +179,9 @@ internal class PropertiesBlock : IBlockParser ShaderPropertyType.Bool2, TokenLexicon.KnownTypes.BOOL3 => ShaderPropertyType.Bool3, TokenLexicon.KnownTypes.BOOL4 => ShaderPropertyType.Bool4, - TokenLexicon.KnownTypes.TEXTURE2D_BINDLESS => ShaderPropertyType.Texture2DBindless, - TokenLexicon.KnownTypes.TEXTURE3D_BINDLESS => ShaderPropertyType.Texture3DBindless, - TokenLexicon.KnownTypes.TEXTURECUBE_BINDLESS => ShaderPropertyType.TextureCubeBindless, + TokenLexicon.KnownTypes.TEXTURE2D => ShaderPropertyType.Texture2D, + TokenLexicon.KnownTypes.TEXTURE3D => ShaderPropertyType.Texture3D, + TokenLexicon.KnownTypes.TEXTURECUBE => ShaderPropertyType.TextureCube, _ => ShaderPropertyType.None, }; } diff --git a/Ghost.Shader/Compiler/SDLCompiler.cs b/Ghost.Shader/Compiler/SDLCompiler.cs index 981a2b5..40abd8a 100644 --- a/Ghost.Shader/Compiler/SDLCompiler.cs +++ b/Ghost.Shader/Compiler/SDLCompiler.cs @@ -54,7 +54,7 @@ internal static class SDLCompiler public static SDLSemantics? SemanticAnalysis(SDLSyntax syntax, out List errors) { - errors = new(); + errors = new List(); if (string.IsNullOrWhiteSpace(syntax.name.lexeme)) { @@ -145,31 +145,23 @@ internal static class SDLCompiler }; } - private static List MergeProperties(List? semantics, List? parent) + private static uint CalculateCBufferSize(List properties) { - var result = new List(); - if (parent != null) - { - result.AddRange(parent); - } + var currentOffset = 0u; - if (semantics != null) + foreach (var prop in properties) { - foreach (var prop in semantics) + var size = prop.type.GetSize(); + + if ((currentOffset % 16) + size > 16) { - if (prop.scope == PropertyScope.Local) - { - result.Add(new PropertyDescriptor - { - name = prop.name, - type = prop.type, - defaultValue = prop.defaultValue - }); - } + currentOffset = (currentOffset + 15u) & ~15u; } + + currentOffset += size; } - return result.DistinctBy(p => p.name).ToList(); + return (currentOffset + 15u) & ~15u; } // TODO: Implement shader inheritance resolution, including property and pass merging. @@ -181,19 +173,23 @@ internal static class SDLCompiler name = semantics.name }; - var shaderGlobalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Global).Select(p => new PropertyDescriptor - { - name = p.name, - type = p.type, - defaultValue = p.defaultValue - }).ToList(); + var shaderGlobalProperties = semantics.properties? + .Where(p => p.scope == PropertyScope.Global) + .Select(p => new PropertyDescriptor + { + name = p.name, + type = p.type, + defaultValue = p.defaultValue + }).ToList(); - var shaderLocalProperties = semantics.properties?.Where(p => p.scope == PropertyScope.Local).Select(p => new PropertyDescriptor - { - name = p.name, - type = p.type, - defaultValue = p.defaultValue - }).ToList(); + var shaderLocalProperties = semantics.properties? + .Where(p => p.scope == PropertyScope.Local) + .Select(p => new PropertyDescriptor + { + name = p.name, + type = p.type, + defaultValue = p.defaultValue + }).ToList(); if (shaderGlobalProperties != null) { @@ -201,13 +197,18 @@ internal static class SDLCompiler descriptor.globalProperties.AddRange(shaderGlobalProperties); } + if (shaderLocalProperties != null) + { + descriptor.properties ??= new List(); + descriptor.properties.AddRange(shaderLocalProperties); + descriptor.cbufferSize = CalculateCBufferSize(descriptor.properties); + } + if (semantics.passes != null) { foreach (var pass in semantics.passes) { var localPipeline = MeragePipeline(pass.localPipeline, PipelineDescriptor.Default); - var localProperties = MergeProperties(pass.localProperties, shaderLocalProperties); // TODO: Merge with base shader properties if inheritance is implemented. - var fullPass = new FullPassDescriptor { uniqueIdentifier = GetPassUniqueId(semantics, pass), @@ -217,9 +218,7 @@ internal static class SDLCompiler pixelShader = pass.pixelShader, localPipeline = localPipeline, defines = pass.defines, - includes = pass.includes, keywords = pass.keywords, - properties = localProperties }; descriptor.passes.Add(fullPass); @@ -258,24 +257,14 @@ internal static class SDLCompiler return Result.Failure("Failed to generate global properties: " + globalPropResult.Message); } - foreach (var pass in desc.passes) + var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory); + if (generatedResult.IsFailure) { - if (pass is not FullPassDescriptor fullPass) - { - continue; - } - - fullPass.includes ??= new List(); - fullPass.includes.Add(globalPropResult.Value); - var generatedResult = GeneratePass(fullPass, generatedOutputDirectory); - if (generatedResult.IsFailure) - { - return Result.Failure("Failed to generate pass files: " + generatedResult.Message); - } - - fullPass.generatedCodePath = generatedResult.Value; + return Result.Failure("Failed to generate pass files: " + generatedResult.Message); } + desc.generatedCodePath = generatedResult.Value; + return desc; } catch (Exception ex) @@ -305,28 +294,23 @@ internal static class SDLCompiler ShaderPropertyType.Bool3 => "bool3", ShaderPropertyType.Bool4 => "bool4", // NOTE: Textures here are bindless, represented as uint (descriptor index). - ShaderPropertyType.Texture2DBindless => "TEXTURE2D_BINDLESS", - ShaderPropertyType.Texture3DBindless => "TEXTURE3D_BINDLESS", - ShaderPropertyType.TextureCubeBindless => "TEXTURECUBE_BINDLESS", - ShaderPropertyType.Texture2DArrayBindless => "TEXTURE2D_ARRAY_BINDLESS", - ShaderPropertyType.TextureCubeArrayBindless => "TEXTURECUBE_ARRAY_BINDLESS", + ShaderPropertyType.Texture2D => "TEXTURE2D_BINDLESS", + ShaderPropertyType.Texture3D => "TEXTURE3D_BINDLESS", + ShaderPropertyType.TextureCube => "TEXTURECUBE_BINDLESS", + ShaderPropertyType.Texture2DArray => "TEXTURE2D_ARRAY_BINDLESS", + ShaderPropertyType.TextureCubeArray => "TEXTURECUBE_ARRAY_BINDLESS", _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported shader property type: {type}") }; } - public static Result GeneratePass(IPassDescriptor descriptor, string targetDirectory) + public static Result GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory) { - if (descriptor is not FullPassDescriptor fullPass) - { - return Result.Failure("Only full pass descriptors are supported for compilation."); - } - if (!Directory.Exists(targetDirectory)) { return Result.Failure("Target directory does not exist."); } - var outputFileName = fullPass.uniqueIdentifier.Replace(' ', '_'); + var outputFileName = descriptor.name.Replace('/', '_'); var outputFilePath = Path.Combine(targetDirectory, outputFileName + ".g.hlsl"); var outputDirectory = Path.GetDirectoryName(outputFilePath); @@ -346,30 +330,17 @@ internal static class SDLCompiler #define {fileDefine} #include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"""); - if (fullPass.includes != null) - { - foreach (var include in fullPass.includes) - { - sb.Append($@" -#include ""{include}"""); - } - sb.AppendLine(); - } - - if (fullPass.properties != null) - { - sb.Append(@" + sb.Append(@" struct PerMaterialData {"); - foreach (var prop in fullPass.properties) - { - sb.Append($@" + foreach (var prop in descriptor.properties) + { + sb.Append($@" {ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};"); - } - sb.Append(@" -};"); } + sb.Append(@" +};"); sb.AppendLine(); sb.AppendLine(@$" @@ -414,4 +385,4 @@ struct GlobalData return globalFilePath; } -} \ No newline at end of file +} diff --git a/Ghost.Shader/Compiler/SDLSemantics.cs b/Ghost.Shader/Compiler/SDLSemantics.cs index 4fa4360..e09b71c 100644 --- a/Ghost.Shader/Compiler/SDLSemantics.cs +++ b/Ghost.Shader/Compiler/SDLSemantics.cs @@ -32,9 +32,7 @@ internal class PassSemantic public ShaderEntryPoint meshShader; public ShaderEntryPoint pixelShader; public List? defines; - public List? includes; public List? keywords; - public List? localProperties; public PipelineSemantic? localPipeline; } diff --git a/Ghost.Shader/Compiler/SDLSyntax.cs b/Ghost.Shader/Compiler/SDLSyntax.cs index 439d93a..c067c5b 100644 --- a/Ghost.Shader/Compiler/SDLSyntax.cs +++ b/Ghost.Shader/Compiler/SDLSyntax.cs @@ -36,9 +36,7 @@ internal class PassSyntax { public Token name; public PipelineSyntax? localPipeline; - public PropertiesSyntax? localProperties; public List? defines; - public List? includes; public List? keywords; public List? functionCalls; } diff --git a/Ghost.Shader/Compiler/Token.cs b/Ghost.Shader/Compiler/Token.cs index 1b943af..ae25f8a 100644 --- a/Ghost.Shader/Compiler/Token.cs +++ b/Ghost.Shader/Compiler/Token.cs @@ -153,6 +153,7 @@ internal static class TokenLexicon public const string FLOAT2 = "float2"; public const string FLOAT3 = "float3"; public const string FLOAT4 = "float4"; + public const string FLOAT4X4 = "float4x4"; public const string INT = "int"; public const string INT2 = "int2"; @@ -170,11 +171,11 @@ internal static class TokenLexicon public const string BOOL4 = "bool4"; // Texture types - public const string TEXTURE2D_BINDLESS = "tex2d_b"; - public const string TEXTURE2D_ARRAY_BINDLESS = "tex2d_arr_b"; - public const string TEXTURE3D_BINDLESS = "tex3d_b"; - public const string TEXTURECUBE_BINDLESS = "texcube_b"; - public const string TEXTURECUBE_ARRAY_BINDLESS = "texcube_arr_b"; + public const string TEXTURE2D = "tex2d"; + public const string TEXTURE2D_ARRAY = "tex2d_arr"; + public const string TEXTURE3D = "tex3d"; + public const string TEXTURECUBE = "texcube"; + public const string TEXTURECUBE_ARRAY = "texcube_arr"; } public static class KnownTextureValue @@ -211,12 +212,12 @@ internal static class TokenLexicon private static readonly HashSet s_types = new() { - KnownTypes.FLOAT, KnownTypes.FLOAT2, KnownTypes.FLOAT3, KnownTypes.FLOAT4, + KnownTypes.FLOAT, KnownTypes.FLOAT2, KnownTypes.FLOAT3, KnownTypes.FLOAT4, KnownTypes.FLOAT4X4, KnownTypes.INT, KnownTypes.INT2, KnownTypes.INT3, KnownTypes.INT4, KnownTypes.UINT, KnownTypes.UINT2, KnownTypes.UINT3, KnownTypes.UINT4, KnownTypes.BOOL, KnownTypes.BOOL2, KnownTypes.BOOL3, KnownTypes.BOOL4, - KnownTypes.TEXTURE2D_BINDLESS, KnownTypes.TEXTURE2D_ARRAY_BINDLESS, KnownTypes.TEXTURE3D_BINDLESS, - KnownTypes.TEXTURECUBE_BINDLESS, KnownTypes.TEXTURECUBE_ARRAY_BINDLESS, + KnownTypes.TEXTURE2D, KnownTypes.TEXTURE2D_ARRAY, KnownTypes.TEXTURE3D, + KnownTypes.TEXTURECUBE, KnownTypes.TEXTURECUBE_ARRAY, }; private static readonly HashSet s_textureDefaultValues = new()