diff --git a/Ghost.Core/Graphics/ShaderDescriptor.cs b/Ghost.Core/Graphics/ShaderDescriptor.cs index d9e9284..74e79ef 100644 --- a/Ghost.Core/Graphics/ShaderDescriptor.cs +++ b/Ghost.Core/Graphics/ShaderDescriptor.cs @@ -77,6 +77,7 @@ 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; @@ -101,4 +102,4 @@ public class ShaderDescriptor public string name = string.Empty; public List globalProperties = new(); public List passes = new(); -} +} \ No newline at end of file diff --git a/Ghost.Core/Utilities/Win32Utility.cs b/Ghost.Core/Utilities/Win32Utility.cs index be1c49a..ab2ea0e 100644 --- a/Ghost.Core/Utilities/Win32Utility.cs +++ b/Ghost.Core/Utilities/Win32Utility.cs @@ -41,13 +41,6 @@ internal static unsafe partial class Win32Utility return new IID_PPV(Windows.__uuidof(), comPtr.PPV()); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IID_PPV IID_PPV_ARGS(T** ppv) - where T : unmanaged, IUnknown.Interface - { - return new IID_PPV(Windows.__uuidof(), (void**)ppv); - } - [Conditional("DEBUG")] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Assert(this HRESULT hr) diff --git a/Ghost.Entities/Ghost.Entities.csproj b/Ghost.Entities/Ghost.Entities.csproj index 6569555..451e5e1 100644 --- a/Ghost.Entities/Ghost.Entities.csproj +++ b/Ghost.Entities/Ghost.Entities.csproj @@ -38,12 +38,6 @@ - - - ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.LowLevel\bin\Release\net9.0\Misaki.HighPerformance.LowLevel.dll - - - TextTemplatingFileGenerator diff --git a/Ghost.Graphics/Core/Material.cs b/Ghost.Graphics/Core/Material.cs index 9fdfbd1..dbb02fe 100644 --- a/Ghost.Graphics/Core/Material.cs +++ b/Ghost.Graphics/Core/Material.cs @@ -63,17 +63,17 @@ public struct Material : IResourceReleasable, IHandleType for (var i = 0; i < shader.PassCount; i++) { var pass = database.GetShaderPass(shader.GetPassKey(i)); - var cbufferInfo = pass.PassPropertyInfo; + var cbufferInfo = pass.CBuffer; var desc = new BufferDesc { - Size = cbufferInfo.Size, + Size = cbufferInfo.SizeInBytes, Usage = BufferUsage.Constant, MemoryType = ResourceMemoryType.Default, }; var buffer = allocator.CreateBuffer(ref desc); - _materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.Size); + _materialPropertiesCache[i] = new CBufferCache(buffer, cbufferInfo.SizeInBytes); } } @@ -118,7 +118,7 @@ public ref struct MaterialAccessor } ref var cache = ref _materialData.GetPassCache(index); - Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.ByteOffset], value); + Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.StartOffset], value); } } diff --git a/Ghost.Graphics/Core/RenderingContext.cs b/Ghost.Graphics/Core/RenderingContext.cs index 572114c..9570c73 100644 --- a/Ghost.Graphics/Core/RenderingContext.cs +++ b/Ghost.Graphics/Core/RenderingContext.cs @@ -10,13 +10,13 @@ namespace Ghost.Graphics.Core; public unsafe readonly ref struct RenderingContext { private readonly IGraphicsEngine _engine; - private readonly ICommandBuffer _directCmb; - private readonly ICommandBuffer _copyCmb; - private readonly ICommandBuffer _computeCmb; + private readonly ICommandBuffer _directCmd; + private readonly ICommandBuffer _copyCmd; + private readonly ICommandBuffer _computeCmd; - public ICommandBuffer DirectCommandBuffer => _directCmb; - public ICommandBuffer CopyCommandBuffer => _copyCmb; - public ICommandBuffer ComputeCommandBuffer => _computeCmb; + public ICommandBuffer DirectCommandBuffer => _directCmd; + public ICommandBuffer CopyCommandBuffer => _copyCmd; + public ICommandBuffer ComputeCommandBuffer => _computeCmd; public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator; public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase; @@ -29,9 +29,9 @@ public unsafe readonly ref struct RenderingContext ICommandBuffer computeCmd) { _engine = engine; - _directCmb = directCmd; - _copyCmb = copyCmd; - _computeCmb = computeCmd; + _directCmd = directCmd; + _copyCmd = copyCmd; + _computeCmd = computeCmd; } public ICommandBuffer CrearteCommandBuffer(CommandBufferType type) @@ -88,29 +88,25 @@ public unsafe readonly ref struct RenderingContext if (needVertexTransition) { - _copyCmb.ResourceBarrier(meshData.vertexBuffer.AsResource(), vertexState, ResourceState.CopyDest); - ResourceDatabase.SetResourceState(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest); + _directCmd.ResourceBarrier(meshData.vertexBuffer.AsResource(), vertexState, ResourceState.CopyDest); } if (needIndexTransition) { - _copyCmb.ResourceBarrier(meshData.indexBuffer.AsResource(), indexState, ResourceState.CopyDest); - ResourceDatabase.SetResourceState(meshData.indexBuffer.AsResource(), ResourceState.CopyDest); + _directCmd.ResourceBarrier(meshData.indexBuffer.AsResource(), indexState, ResourceState.CopyDest); } - _copyCmb.UploadBuffer(meshData.vertexBuffer, meshData.vertices.AsSpan()); - _copyCmb.UploadBuffer(meshData.indexBuffer, meshData.indices.AsSpan()); + _directCmd.UploadBuffer(meshData.vertexBuffer, meshData.vertices.AsSpan()); + _directCmd.UploadBuffer(meshData.indexBuffer, meshData.indices.AsSpan()); if (needVertexTransition) { - _copyCmb.ResourceBarrier(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest, vertexState); - ResourceDatabase.SetResourceState(meshData.vertexBuffer.AsResource(), vertexState); + _directCmd.ResourceBarrier(meshData.vertexBuffer.AsResource(), ResourceState.CopyDest, vertexState); } if (needIndexTransition) { - ResourceDatabase.SetResourceState(meshData.indexBuffer.AsResource(), indexState); - _copyCmb.ResourceBarrier(meshData.indexBuffer.AsResource(), ResourceState.CopyDest, indexState); + _directCmd.ResourceBarrier(meshData.indexBuffer.AsResource(), ResourceState.CopyDest, indexState); } if (markMeshStatic) @@ -141,20 +137,18 @@ public unsafe readonly ref struct RenderingContext if (needTransition) { - _copyCmb.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest); - ResourceDatabase.SetResourceState(texture.AsResource(), ResourceState.CopyDest); + _directCmd.ResourceBarrier(texture.AsResource(), sateBefore, ResourceState.CopyDest); } - _copyCmb.UploadTexture(texture, subresourceData); + _directCmd.UploadTexture(texture, subresourceData); if (needTransition) { - _copyCmb.ResourceBarrier(texture.AsResource(), ResourceState.CopyDest, sateBefore); - ResourceDatabase.SetResourceState(texture.AsResource(), sateBefore); + _directCmd.ResourceBarrier(texture.AsResource(), ResourceState.CopyDest, sateBefore); } } - // TODO: Ideally we should queue the draw call to our rendering system, and render it in the full rendering pipeline. + // TODO: Ideally we should queue the draw call to our rendering system, and render it in a full rendering pipeline. // This is just a place holder for now for testing purpose. public void DispatchMesh(Handle mesh, Handle material, string passName, uint numThreadsX) { @@ -172,20 +166,20 @@ public unsafe readonly ref struct RenderingContext hash.rtvFormats[0] = TextureFormat.B8G8R8A8_UNorm; var pipelineKey = hash.GetKey(); - _directCmb.SetPipelineState(pipelineKey); + _directCmd.SetPipelineState(pipelineKey); // NOTE: We use fixed root signature layout for bindless rendering. ref var cache = ref materialRef.GetPassCache(passIndex); - _directCmb.SetConstantBufferView(RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT, cache.GpuResource); + _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: Matbe handle the transitional bindless model? + // TODO: Maybe handle the traditional bindless model? #if false var samplerGpuHandle = _descriptorAllocator.GetSamplerHeap()->GetGPUDescriptorHandleForHeapStart(); _commandList.Get()->SetGraphicsRootDescriptorTable(rootParamIndex, samplerGpuHandle); #endif var threadGroupCountX = ((uint)meshRef.indices.Count + numThreadsX - 1) / numThreadsX; - _directCmb.DispatchMesh(threadGroupCountX, 1, 1); + _directCmd.DispatchMesh(threadGroupCountX, 1, 1); } } diff --git a/Ghost.Graphics/Core/Shader.cs b/Ghost.Graphics/Core/Shader.cs index d0d59ce..d5c6665 100644 --- a/Ghost.Graphics/Core/Shader.cs +++ b/Ghost.Graphics/Core/Shader.cs @@ -7,66 +7,23 @@ using System.Runtime.InteropServices; namespace Ghost.Graphics.Core; -public readonly struct TextureInfo -{ - public uint RegisterSlot - { - get; init; - } - - public uint RootParameterIndex - { - get; init; - } -} - -public readonly struct PropertyInfo -{ - public uint CBufferIndex - { - get; init; - } - - public uint ByteOffset - { - get; init; - } - - public uint Size - { - get; init; - } -} - -public readonly struct CBufferInfo -{ - public uint Size - { - get; init; - } - - public uint RegisterSlot - { - get; init; - } -} - -public unsafe class ShaderPass : IResourceReleasable +public class 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; - private readonly UnsafeList _properties; - internal CBufferInfo PassPropertyInfo - { - get; - } + public CBufferInfo CBuffer => _cbufferInfo; - public ShaderPass(CBufferInfo info, UnsafeList properties, Dictionary propertyNameToIdMap) + public ShaderPass(CBufferInfo info) { - PassPropertyInfo = info; - _properties = properties; - _propertyLookup = propertyNameToIdMap; + _cbufferInfo = info; + + _propertyLookup = new Dictionary(info.Properties.Count); + for (var i = 0; i < info.Properties.Count; i++) + { + _propertyLookup[info.Properties[i].Name] = i; + } } public int GetPropertyId(string propertyName) @@ -74,19 +31,18 @@ public unsafe class ShaderPass : IResourceReleasable return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1; } - public PropertyInfo GetPropertyInfo(int id) + public CBufferPropertyInfo GetPropertyInfo(int id) { - return _properties[id]; + return _cbufferInfo.Properties[id]; } - public PropertyInfo GetPropertyInfo(string propertyName) + public CBufferPropertyInfo GetPropertyInfo(string propertyName) { - return _properties[GetPropertyId(propertyName)]; + return _cbufferInfo.Properties[GetPropertyId(propertyName)]; } void IResourceReleasable.ReleaseResource(IResourceDatabase database) { - _properties.Dispose(); } } diff --git a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs index b10e01a..ec06df9 100644 --- a/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs +++ b/Ghost.Graphics/D3D12/D3D12CommandBuffer.cs @@ -307,7 +307,7 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer ThrowIfNotRecording(); IncrementCommandCount(); - var shaderPipeline = _pipelineLibrary.LoadGraphicsPSO(pipelineKey).GetValueOrThrow(); + var shaderPipeline = _pipelineLibrary.GetGraphicsPSO(pipelineKey).GetValueOrThrow(); _commandList.Get()->SetPipelineState(shaderPipeline.value); } diff --git a/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs b/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs index 925c246..f57f98d 100644 --- a/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs +++ b/Ghost.Graphics/D3D12/D3D12GraphicsEngine.cs @@ -12,9 +12,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine #endif private readonly D3D12RenderDevice _device; - private readonly D3D12PipelineLibrary _pipelineLibrary; private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12ResourceDatabase _resourceDatabase; + private readonly D3D12PipelineLibrary _pipelineLibrary; private readonly D3D12ResourceAllocator _resourceAllocator; private readonly D3D12CommandBuffer _copyCommandBuffer; @@ -37,9 +37,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine _descriptorAllocator = new(_device); _resourceDatabase = new(_descriptorAllocator); - _resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase); - _pipelineLibrary = new(_device, _resourceDatabase); + _resourceAllocator = new(renderSystem, _device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary); + _copyCommandBuffer = new( _device, _pipelineLibrary, @@ -112,6 +112,8 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine { renderer.ExecutePendingResize(); } + + _copyCommandBuffer.Begin(); } public void RenderFrame() @@ -127,6 +129,8 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine public void EndFrame() { ThrowIfDisposed(); + + _copyCommandBuffer.End(); _resourceAllocator.ReleaseTempResources(); } @@ -138,9 +142,9 @@ internal unsafe class D3D12GraphicsEngine : IGraphicsEngine } _copyCommandBuffer.Dispose(); - _pipelineLibrary.Dispose(); _resourceAllocator.Dispose(); + _pipelineLibrary.Dispose(); _resourceDatabase.Dispose(); _descriptorAllocator.Dispose(); diff --git a/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs b/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs index 3c4b0f1..5975931 100644 --- a/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs +++ b/Ghost.Graphics/D3D12/D3D12PipelineLibrary.cs @@ -7,7 +7,6 @@ using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.Utilities; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; @@ -22,6 +21,7 @@ internal struct D3D12GraphicsCompiledResult : IDisposable public CompileResult tsResult; public CompileResult msResult; public CompileResult psResult; + public CBufferInfo cbufferInfo; public void Dispose() { @@ -33,14 +33,12 @@ internal struct D3D12GraphicsCompiledResult : IDisposable internal struct D3D12PipelineState : IDisposable { - // NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later. - public D3D12GraphicsCompiledResult compileResult; public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc; public ComPtr pso; + public ShaderPassKey shaderPass; public void Dispose() { - compileResult.Dispose(); pso.Dispose(); } } @@ -62,6 +60,8 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable private ComPtr _defaultRootSignature; private readonly Dictionary _pipelineCache; + // NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later. + private readonly Dictionary _compiledResults; public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get(); @@ -71,6 +71,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable _resourceDatabase = resourceDatabase; _pipelineCache = new(); + _compiledResults = new(); CreateDefaultRootSignature(); } @@ -214,7 +215,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable fs.Write(buffer.AsSpan()); } - private static void ValidateReflectionData(ShaderReflectionData reflectionData) + private static CBufferInfo ValidateReflectionData(FullPassDescriptor descriptor, ShaderReflectionData reflectionData) { if (reflectionData.ConstantBuffers.Count != rootParamCount) { @@ -226,27 +227,27 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable throw new NotSupportedException("Shader reflection data contains unsupported resource types. Only constant buffers are supported in the current root signature."); } + return reflectionData.ConstantBuffers[RootSignatureLayout.PER_MATERIAL_BUFFER_SLOT]; + // TODO: Validate Cbuffer sizes and bindings. } - private static Result CompileAndValidateFullPass(FullPassDescriptor descriptor) + private static D3D12GraphicsCompiledResult CompileAndValidateFullPass(FullPassDescriptor descriptor) { - static CompileResult CompileAndValidate(ref CompilerConfig config) + static CompileResult CompileAndValidate(ref CompilerConfig config, FullPassDescriptor descriptor) { IDxcBlob* reflectionBlob = default; + CBufferInfo cbufferInfo = default; try { // TODO: This does not include generated code. This will cause a root signature mismatch. var result = D3D12ShaderCompiler.Compile(ref config, Allocator.Persistent, &reflectionBlob).GetValueOrThrow(); - -#if false if (reflectionBlob != null) { var reflection = D3D12ShaderCompiler.PerformDXCReflection(reflectionBlob).GetValueOrThrow(); - ValidateReflectionData(reflection); + cbufferInfo = ValidateReflectionData(descriptor, reflection); } -#endif return result; } @@ -266,7 +267,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable var config = new CompilerConfig { defines = descriptor.defines.AsSpan(), - includes = descriptor.includes.AsSpan(), + include = descriptor.generatedCodePath, shaderPath = tsEntry.shader, entryPoint = tsEntry.entry, stage = ShaderStage.TaskShader, @@ -275,7 +276,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable options = CompilerOption.KeepReflections, }; - tsResult = CompileAndValidate(ref config); + tsResult = CompileAndValidate(ref config, descriptor); } CompileResult msResult; @@ -285,7 +286,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable var config = new CompilerConfig { defines = descriptor.defines.AsSpan(), - includes = descriptor.includes.AsSpan(), + include = descriptor.generatedCodePath, shaderPath = msEntry.shader, entryPoint = msEntry.entry, stage = ShaderStage.MeshShader, @@ -294,11 +295,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable options = CompilerOption.KeepReflections, }; - msResult = CompileAndValidate(ref config); + msResult = CompileAndValidate(ref config, descriptor); } else { - return Result.Fail("Mesh shader expected."); + throw new InvalidOperationException("Mesh shader expected."); } CompileResult psResult; @@ -308,7 +309,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable var config = new CompilerConfig { defines = descriptor.defines.AsSpan(), - includes = descriptor.includes.AsSpan(), + include = descriptor.generatedCodePath, shaderPath = psEntry.shader, entryPoint = psEntry.entry, stage = ShaderStage.PixelShader, @@ -317,11 +318,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable options = CompilerOption.KeepReflections, }; - psResult = CompileAndValidate(ref config); + psResult = CompileAndValidate(ref config, descriptor); } else { - return Result.Fail("Pixel shader expected."); + throw new InvalidOperationException("Pixel shader expected."); } return new D3D12GraphicsCompiledResult @@ -353,6 +354,11 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp); } + private bool TryGetCompiledCache(ShaderPassKey passKey, out D3D12GraphicsCompiledResult compiled) + { + return _compiledResults.TryGetValue(passKey, out compiled); + } + private GraphicsPipelineKey CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly D3D12GraphicsCompiledResult compiled) { var rtvCount = (uint)Math.Min(descriptor.rtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT); @@ -412,7 +418,6 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable ref var existing = ref CollectionsMarshal.GetValueRefOrAddDefault(_pipelineCache, key, out var exists); if (!exists) { - existing.compileResult = compiled; existing.psoDesc = desc; var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc); @@ -448,14 +453,22 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable public GraphicsPipelineKey CompilePassPSO(IPassDescriptor descriptor, ReadOnlySpan rtvs, TextureFormat dsv) { - var key = default(GraphicsPipelineKey); + GraphicsPipelineKey key = default; + + var passKey = new ShaderPassKey(descriptor.Identifier); + var hasCompiledCache = TryGetCompiledCache(passKey, out var compiled); + switch (descriptor) { case FullPassDescriptor fullPass: - var result = CompileAndValidateFullPass(fullPass).GetValueOrThrow(); + if (!hasCompiledCache) + { + compiled = CompileAndValidateFullPass(fullPass); + } + var psoDes = new GraphicsPSODescriptor { - passId = new(fullPass.Identifier), + passId = new ShaderPassKey(fullPass.Identifier), zTest = fullPass.localPipeline.zTest, zWrite = fullPass.localPipeline.zWrite, cull = fullPass.localPipeline.cull, @@ -466,10 +479,17 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable dsvFormat = dsv, }; - key = CompilePSO(in psoDes, in result); + key = CompilePSO(in psoDes, in compiled); break; // Do we need to support other pass types? + case FallbackPassDescriptor: + if (!hasCompiledCache) + { + throw new ArgumentException("FallbackPassDescriptor is not supported for PSO compilation. There may be some inheritance dependency issues."); + } + + break; default: break; @@ -478,15 +498,24 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary, IDisposable return key; } - public Result> LoadGraphicsPSO(GraphicsPipelineKey key) + public Result> GetGraphicsPSO(GraphicsPipelineKey key) { - ref var cacheEntry = ref CollectionsMarshal.GetValueRefOrNullRef(_pipelineCache, key); - if (Unsafe.IsNullRef(ref cacheEntry)) + if (_pipelineCache.TryGetValue(key, out var cacheEntry)) { - return Result.Fail("Pipeline state not found in cache."); + return new Ptr(cacheEntry.pso.Get()); } - return new Ptr(cacheEntry.pso.Get()); + return Result.Fail("Pipeline state not found in cache."); + } + + public Result GetCBufferInfo(ShaderPassKey key) + { + if (_compiledResults.TryGetValue(key, out var compiled)) + { + return compiled.cbufferInfo; + } + + return Result.Fail("Compiled shader not found in cache."); } public void Dispose() diff --git a/Ghost.Graphics/D3D12/D3D12Renderer.cs b/Ghost.Graphics/D3D12/D3D12Renderer.cs index c0f4c0e..9359dfc 100644 --- a/Ghost.Graphics/D3D12/D3D12Renderer.cs +++ b/Ghost.Graphics/D3D12/D3D12Renderer.cs @@ -210,6 +210,13 @@ internal unsafe class D3D12Renderer : IRenderer clearStencil = 0, }; + // NOTE: Testing only. + var ctx = new RenderingContext(_graphicsEngine, cmd, _graphicsEngine.CopyCommandBuffer, null!); + if (_frameIndex == 0) + { + _pass.Initialize(ref ctx); + } + cmd.BeginRenderPass(rtDesc, depthDesc, false); var viewport = new ViewportDesc { width = _currentSize.x, height = _currentSize.y, minDepth = 0, maxDepth = 1 }; @@ -219,12 +226,6 @@ internal unsafe class D3D12Renderer : IRenderer cmd.SetScissorRect(scissor); // NOTE: Testing only. - var ctx = new RenderingContext(_graphicsEngine, cmd, _graphicsEngine.CopyCommandBuffer, null!); - if (_frameIndex == 0) - { - _pass.Initialize(ref ctx); - } - _pass.Execute(ref ctx); cmd.EndRenderPass(); diff --git a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs index ef61d1d..4ce7156 100644 --- a/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs +++ b/Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs @@ -597,13 +597,19 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator private readonly D3D12RenderDevice _device; private readonly D3D12DescriptorAllocator _descriptorAllocator; private readonly D3D12ResourceDatabase _resourceDatabase; + private readonly D3D12PipelineLibrary _pipelineLibrary; private ComPtr _allocator; private UnsafeQueue> _temResources; private bool _disposed; - public D3D12ResourceAllocator(IFenceSynchronizer fenceSynchronizer, D3D12RenderDevice device, D3D12DescriptorAllocator descriptorAllocator, D3D12ResourceDatabase resourceDatabase) + public D3D12ResourceAllocator( + IFenceSynchronizer fenceSynchronizer, + D3D12RenderDevice device, + D3D12DescriptorAllocator descriptorAllocator, + D3D12ResourceDatabase resourceDatabase, + D3D12PipelineLibrary pipelineLibrary) { var desc = new D3D12MA_ALLOCATOR_DESC { @@ -616,10 +622,11 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator)); _allocator.Attach(pAllocator); - _device = device; _fenceSynchronizer = fenceSynchronizer; + _device = device; _descriptorAllocator = descriptorAllocator; _resourceDatabase = resourceDatabase; + _pipelineLibrary = pipelineLibrary; _temResources = new(64, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); } @@ -869,6 +876,18 @@ internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator public Identifier CreateShader(ShaderDescriptor descriptor) { var shader = new Shader(descriptor); + foreach (var pass in descriptor.passes) + { + if (pass is not FullPassDescriptor fullPass) + { + continue; + } + + var passKey = new ShaderPassKey(fullPass.uniqueIdentifier); + var cbufferInfo = _pipelineLibrary.GetCBufferInfo(passKey).GetValueOrThrow(); + _resourceDatabase.AddShaderPass(new ShaderPassKey(fullPass.uniqueIdentifier), new ShaderPass(cbufferInfo)); + } + return _resourceDatabase.AddShader(shader); } diff --git a/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs b/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs index 0497951..53af9c7 100644 --- a/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs +++ b/Ghost.Graphics/D3D12/D3D12ShaderCompiler.cs @@ -1,13 +1,13 @@ using Ghost.Core; -using Ghost.Core.Utilities; using Ghost.Graphics.RHI; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using TerraFX.Interop.DirectX; using TerraFX.Interop.Windows; +using static TerraFX.Interop.DirectX.DXC; + namespace Ghost.Graphics.D3D12; internal struct CompileResult : IDisposable @@ -22,52 +22,6 @@ internal struct CompileResult : IDisposable } } -internal readonly struct CBufferVariableInfo -{ - public string Name - { - get; init; - } - - public uint StartOffset - { - get; init; - } - - public uint Size - { - get; init; - } -} - -internal readonly struct CBufferInfo -{ - public string Name - { - get; init; - } - - public uint RegisterSlot - { - get; init; - } - - public uint RegisterSpace - { - get; init; - } - - public uint SizeInBytes - { - get; init; - } - - public IReadOnlyList Variables - { - get; init; - } -} - internal readonly struct ResourceBindingInfo { public string Name @@ -145,10 +99,10 @@ internal static unsafe class D3D12ShaderCompiler { return level switch { - CompilerOptimizeLevel.O0 => "-O0", - CompilerOptimizeLevel.O1 => "-O1", - CompilerOptimizeLevel.O2 => "-O2", - CompilerOptimizeLevel.O3 => "-O3", + CompilerOptimizeLevel.O0 => DXC_ARG_OPTIMIZATION_LEVEL0, + CompilerOptimizeLevel.O1 => DXC_ARG_OPTIMIZATION_LEVEL1, + CompilerOptimizeLevel.O2 => DXC_ARG_OPTIMIZATION_LEVEL2, + CompilerOptimizeLevel.O3 => DXC_ARG_OPTIMIZATION_LEVEL3, _ => throw new ArgumentOutOfRangeException(nameof(level), "Unsupported optimization level") }; } @@ -164,18 +118,20 @@ internal static unsafe class D3D12ShaderCompiler GetOptimizeLevelString(config.optimizeLevel), // Optimization level }; - foreach (var include in config.includes) - { - argsArray.Add("-I"); - argsArray.Add(include); - } - foreach (var define in config.defines) { argsArray.Add("-D"); argsArray.Add(define); } + // HACK: Currently DXC does not support force include, we have to use GENERATED_CODE_PATH define as a workaround. + // User must to write '#include GENERATED_CODE_PATH' in their shader code manually. + if (File.Exists(config.include)) + { + argsArray.Add("-D"); + argsArray.Add($"GENERATED_CODE_PATH={'"' + config.include.Replace("\\", "/") + '"'}"); + } + if (!config.options.HasFlag(CompilerOption.KeepDebugInfo)) { argsArray.Add("-Qstrip_debug"); @@ -188,7 +144,7 @@ internal static unsafe class D3D12ShaderCompiler if (config.options.HasFlag(CompilerOption.WarnAsError)) { - argsArray.Add("-WX"); + argsArray.Add(DXC_ARG_WARNINGS_ARE_ERRORS); } return argsArray; @@ -210,8 +166,7 @@ internal static unsafe class D3D12ShaderCompiler ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler)); ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils)); - //pIncludeHandler.Get()->LoadSource(); - pUtils->CreateDefaultIncludeHandler(&pIncludeHandler); + ThrowIfFailed(pUtils->CreateDefaultIncludeHandler(&pIncludeHandler)); // Create source blob using ComPtr sourceBlob = default; @@ -239,7 +194,7 @@ internal static unsafe class D3D12ShaderCompiler { Ptr = sourceBlob.Get()->GetBufferPointer(), Size = sourceBlob.Get()->GetBufferSize(), - Encoding = DXC.DXC_CP_UTF8 + Encoding = DXC_CP_UTF8 }; ThrowIfFailed(pCompiler->Compile(&buffer, argPtrs, (uint)argsArray.Count, pIncludeHandler, __uuidof(pResult), (void**)&pResult)); @@ -268,7 +223,7 @@ internal static unsafe class D3D12ShaderCompiler using ComPtr bytecodeBlob = default; ThrowIfFailed(pResult->GetResult(bytecodeBlob.GetAddressOf())); - // Get reflection data using DXC API + // Get pReflection data using DXC API if (ppReflectionBlob != null) { ThrowIfFailed(pResult->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof(), (void**)ppReflectionBlob, null)); @@ -302,8 +257,8 @@ internal static unsafe class D3D12ShaderCompiler } } - // TODO: Since we are using fixed root signature layout, the reflection pass should only validate the layout, not generate it. - // TODO: Ideally this should return a structured reflection data instead of populating raw lists/dictionaries. + // TODO: Since we are using fixed root signature layout, the pReflection pass should only validate the layout, not generate it. + // TODO: Ideally this should return a structured pReflection data instead of populating raw lists/dictionaries. public static Result PerformDXCReflection(IDxcBlob* reflectionBlob) { if (reflectionBlob == null) @@ -311,34 +266,34 @@ internal static unsafe class D3D12ShaderCompiler return Result.Fail("Reflection blob is null."); } - ComPtr utils = default; - ComPtr reflection = default; + IDxcUtils* pUtils = default; + ID3D12ShaderReflection* pReflection = default; try { - // Create DXC pUtils to parse reflection data + // Create DXC pUtils to parse pReflection data var dxcuID = CLSID.CLSID_DxcUtils; - ThrowIfFailed(DxcCreateInstance(&dxcuID, utils.IID(), utils.PPV())); + ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils)); - // Create reflection interface from blob + // Create pReflection interface from blob var reflectionBuffer = new DxcBuffer { Ptr = reflectionBlob->GetBufferPointer(), Size = reflectionBlob->GetBufferSize(), - Encoding = DXC.DXC_CP_ACP + Encoding = DXC_CP_ACP }; - ThrowIfFailed(utils.Get()->CreateReflection(&reflectionBuffer, reflection.IID(), reflection.PPV())); + ThrowIfFailed(pUtils->CreateReflection(&reflectionBuffer, __uuidof(pReflection), (void**)&pReflection)); D3D12_SHADER_DESC shaderDesc; - ThrowIfFailed(reflection.Get()->GetDesc(&shaderDesc)); + ThrowIfFailed(pReflection->GetDesc(&shaderDesc)); var reflectionData = new ShaderReflectionData(); for (uint i = 0; i < shaderDesc.BoundResources; i++) { D3D12_SHADER_INPUT_BIND_DESC bindDesc; - ThrowIfFailed(reflection.Get()->GetResourceBindingDesc(i, &bindDesc)); + ThrowIfFailed(pReflection->GetResourceBindingDesc(i, &bindDesc)); var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name); if (resourceName == null) @@ -350,11 +305,11 @@ internal static unsafe class D3D12ShaderCompiler { case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER: { - var cbuffer = reflection.Get()->GetConstantBufferByName(bindDesc.Name); + var cbuffer = pReflection->GetConstantBufferByName(bindDesc.Name); D3D12_SHADER_BUFFER_DESC cbufferDesc; ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc)); - var variables = new List((int)cbufferDesc.Variables); + var variables = new List((int)cbufferDesc.Variables); // Now we iterate all variables for *every* cbuffer, not just b3 for (uint j = 0; j < cbufferDesc.Variables; j++) @@ -369,7 +324,7 @@ internal static unsafe class D3D12ShaderCompiler continue; } - variables.Add(new CBufferVariableInfo + variables.Add(new CBufferPropertyInfo { Name = variableName, StartOffset = varDesc.StartOffset, @@ -383,7 +338,7 @@ internal static unsafe class D3D12ShaderCompiler RegisterSlot = bindDesc.BindPoint, RegisterSpace = bindDesc.Space, SizeInBytes = cbufferDesc.Size, - Variables = variables + Properties = variables }); break; @@ -411,8 +366,8 @@ internal static unsafe class D3D12ShaderCompiler } finally { - utils.Dispose(); - reflection.Dispose(); + pUtils->Release(); + pReflection->Release(); } } } diff --git a/Ghost.Graphics/RHI/Common.cs b/Ghost.Graphics/RHI/Common.cs index bf2aa39..240454b 100644 --- a/Ghost.Graphics/RHI/Common.cs +++ b/Ghost.Graphics/RHI/Common.cs @@ -1,12 +1,12 @@ using Ghost.Core; using Ghost.Core.Graphics; +using Ghost.Graphics.Core; using Ghost.Graphics.D3D12.Utilities; using Misaki.HighPerformance.Utilities; using System.IO.Hashing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using TerraFX.Interop.DirectX; -using Ghost.Graphics.Core; namespace Ghost.Graphics.RHI; @@ -122,6 +122,51 @@ public ref struct GraphicsPSODescriptor public TextureFormat dsvFormat; } +public readonly struct CBufferPropertyInfo +{ + public string Name + { + get; init; + } + + public uint StartOffset + { + get; init; + } + + public uint Size + { + get; init; + } +} + +public readonly struct CBufferInfo +{ + public string Name + { + get; init; + } + + public uint RegisterSlot + { + get; init; + } + + public uint RegisterSpace + { + get; init; + } + + public uint SizeInBytes + { + get; init; + } + + public IReadOnlyList Properties + { + get; init; + } +} public struct ViewportDesc { @@ -685,8 +730,8 @@ public enum PrimitiveTopology internal ref struct CompilerConfig { - public ReadOnlySpan includes; public ReadOnlySpan defines; + public string? include; public string shaderPath; public string entryPoint; public ShaderStage stage; diff --git a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs index 7a6ce37..d44313f 100644 --- a/Ghost.Graphics/RenderPasses/MeshRenderPass.cs +++ b/Ghost.Graphics/RenderPasses/MeshRenderPass.cs @@ -28,7 +28,7 @@ internal unsafe class MeshRenderPass : IRenderPass public void Initialize(ref readonly RenderingContext ctx) { - var shaderDescriptor = SDLCompiler.CompileShader("F:\\csharp\\GhostEngine\\Ghost.Graphics\\test.gshader").GetValueOrThrow(); + var shaderDescriptor = SDLCompiler.CompileShader("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader", "C:/Users/Misaki/Downloads/Archive").GetValueOrThrow(); var key = ctx.PipelineLibrary.CompilePassPSO(shaderDescriptor.passes[0], [TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown); diff --git a/Ghost.Graphics/RenderPasses/ShaderCode.hlsl b/Ghost.Graphics/RenderPasses/ShaderCode.hlsl index c9ca04e..916c776 100644 --- a/Ghost.Graphics/RenderPasses/ShaderCode.hlsl +++ b/Ghost.Graphics/RenderPasses/ShaderCode.hlsl @@ -1,13 +1,6 @@ -cbuffer ConstantBuffer : register(b0) -{ - float4 _Color; - uint _TextureIndex1; - uint _TextureIndex2; - uint _TextureIndex3; - uint _TextureIndex4; - uint _VertexBufferIndex; - uint _IndexBufferIndex; -}; + +#include GENERATED_CODE_PATH +#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Properties.hlsl" struct Vertex { @@ -34,8 +27,8 @@ void MSMain( out indices uint3 outTris[1]) { // Fetch bindless buffers - ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[_VertexBufferIndex]; - ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[_IndexBufferIndex]; + ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[g_PerMaterialData.vertexBufferIndex]; + ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[g_PerMaterialData.indexBufferIndex]; // Compute the triangle’s vertex indices uint vertexId = groupThreadID.x; @@ -67,11 +60,11 @@ void MSMain( float4 PSMain(PixelInput input) : SV_TARGET { - float4 color1 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex1, 0, input.uv.xy); - float4 color2 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex2, 0, input.uv.xy); - float4 color3 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex3, 0, input.uv.xy); - float4 color4 = SAMPLE_TEXTURE2D_BINDLESS(_TextureIndex4, 0, input.uv.xy); + 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 blendedColor = (color1 + color2 + color3 + color4) * 0.25f; - return blendedColor * _Color; + return blendedColor * g_PerMaterialData.color; } diff --git a/Ghost.Graphics/test.gshader b/Ghost.Graphics/test.gshader index cba2589..ae2942d 100644 --- a/Ghost.Graphics/test.gshader +++ b/Ghost.Graphics/test.gshader @@ -7,6 +7,8 @@ shader "MyShader/Standard" tex2d_b texture2 = tex2d_b(white); tex2d_b texture3 = tex2d_b(grey); tex2d_b texture4 = tex2d_b(normal); + uint vertexBufferIndex; + uint indexBufferIndex; } pipeline @@ -22,10 +24,5 @@ shader "MyShader/Standard" { ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain"); ps("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "PSMain"); - - includes - { - "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"; - } } -} +} \ No newline at end of file diff --git a/Ghost.Shader/BuiltIn/Common.hlsl b/Ghost.Shader/BuiltIn/Common.hlsl index 6c73a40..724f68a 100644 --- a/Ghost.Shader/BuiltIn/Common.hlsl +++ b/Ghost.Shader/BuiltIn/Common.hlsl @@ -1,4 +1,4 @@ -#ifndef COMMON_HLSL +#ifndef COMMON_HLSL #define COMMON_HLSL #undef USE_TRADITIONAL_BINDLESS // Just for testing, this should be handled by engine feature level. @@ -43,11 +43,11 @@ #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 TEXTURE2D Texture2D +#define TEXTURE3D Texture3D +#define TEXTURECUBE TextureCube +#define TEXTURE2D_ARRAY Texture2DArray +#define TEXTURECUBE_ARRAY TextureCubeArray #define SAMPLER SamplerState @@ -69,11 +69,26 @@ #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) GET_TEXTURE2D_BINDLESS(texId).Sample(GET_BINDLESS_SAMPLER(sampId), uv) -#define SAMPLE_TEXTURE2D_LEVEL_BINDLESS(texId, sampId, uv, level) GET_TEXTURE2D_BINDLESS(texId).SampleLevel(GET_BINDLESS_SAMPLER(sampId), 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_BINDLESS_SAMPLER(sampId), 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); +} + diff --git a/Ghost.Shader/BuiltIn/Properties.hlsl b/Ghost.Shader/BuiltIn/Properties.hlsl index 0dfaf44..d18f54b 100644 --- a/Ghost.Shader/BuiltIn/Properties.hlsl +++ b/Ghost.Shader/BuiltIn/Properties.hlsl @@ -1,4 +1,4 @@ -#ifndef PROPERTIES_HLSL +#ifndef PROPERTIES_HLSL #define PROPERTIES_HLSL #include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl" diff --git a/Ghost.Shader/Compiler/SDLCompiler.cs b/Ghost.Shader/Compiler/SDLCompiler.cs index 326e435..ad2eb9a 100644 --- a/Ghost.Shader/Compiler/SDLCompiler.cs +++ b/Ghost.Shader/Compiler/SDLCompiler.cs @@ -11,7 +11,7 @@ public struct SDLError public int line; public int column; - public readonly override string ToString() + public override readonly string ToString() { return $"Error at {line}:{column} - {message}"; } @@ -125,24 +125,6 @@ internal static class SDLCompiler private static string GetPassUniqueId(SDLSemantics shader, PassSemantic pass) { - //static ulong Fnv1a64(ReadOnlySpan data) - //{ - // const ulong offset = 14695981039346656037; - // const ulong prime = 1099511628211; - - // var hash = offset; - - // foreach (var b in data) - // { - // hash ^= b; - // hash *= prime; - // } - - // return hash; - //} - - //return $"{Fnv1a64(shader.name)}_{pass.name}"; - return $"{shader.name}_{pass.name}"; } @@ -215,6 +197,7 @@ internal static class SDLCompiler if (shaderGlobalProperties != null) { + descriptor.globalProperties ??= new List(); descriptor.globalProperties.AddRange(shaderGlobalProperties); } @@ -245,27 +228,49 @@ internal static class SDLCompiler return descriptor; } - public static Result CompileShader(string shaderPath) + public static Result CompileShader(string shaderPath, string generatedOutputDirectory) { - var source = File.ReadAllText(shaderPath); - - var lexer = new Lexer(source); - var stream = new TokenStream(lexer.Tokenize()); - var shaderInfo = ParseShaders(stream); - var model = SemanticAnalysis(shaderInfo[0], out var errors); - - if (errors.Count != 0 || model == null) + try { - var errorMessages = new StringBuilder(); - foreach (var error in errors) + var source = File.ReadAllText(shaderPath); + + var lexer = new Lexer(source); + var stream = new TokenStream(lexer.Tokenize()); + var shaderInfo = ParseShaders(stream); + var model = SemanticAnalysis(shaderInfo[0], out var errors); + + if (errors.Count != 0 || model == null) { - errorMessages.AppendLine(error.ToString()); + var errorMessages = new StringBuilder(); + foreach (var error in errors) + { + errorMessages.AppendLine(error.ToString()); + } + + return Result.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString()); } - return Result.Fail("Failed to compile shader due to errors:\n" + errorMessages.ToString()); - } + var desc = ResolveShader(model); + var globalPropPath = GenerateGlobalProperties(desc.globalProperties, generatedOutputDirectory); - return ResolveShader(model); + foreach (var pass in desc.passes) + { + if (pass is not FullPassDescriptor fullPass) + { + continue; + } + + fullPass.includes ??= new List(); + fullPass.includes.Add(globalPropPath); + fullPass.generatedCodePath = GeneratePass(fullPass, generatedOutputDirectory); + } + + return desc; + } + catch (Exception ex) + { + return Result.Fail("Failed to generate shader files: " + ex.Message); + } } private static string ShaderPropertyTypeToHLSLType(ShaderPropertyType type) @@ -330,6 +335,16 @@ 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) { @@ -354,23 +369,20 @@ struct PerMaterialData return outputFilePath; } - public static void GenerateShader(ShaderDescriptor descriptor, string targetDirectory) + public static string GenerateGlobalProperties(List globalProperties, string targetDirectory) { if (!Directory.Exists(targetDirectory)) { throw new ArgumentException("Target directory does not exist.", nameof(targetDirectory)); } - // Generate global property file. - if (descriptor.globalProperties.Count > 0) - { - var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME); - using var globalFileStream = File.CreateText(globalFilePath); + var globalFilePath = Path.Combine(targetDirectory, _GLOBAL_PROPERTY_FILE_NAME); + using var globalFileStream = File.CreateText(globalFilePath); - var sb = new StringBuilder(); + var sb = new StringBuilder(); - sb.AppendLine(_GENERATED_FILE_HEADER); - sb.Append(@" + sb.AppendLine(_GENERATED_FILE_HEADER); + sb.Append(@" #ifndef GLOBALDATA_G_HLSL #define GLOBALDATA_G_HLSL @@ -378,22 +390,17 @@ struct PerMaterialData struct GlobalData {"); - foreach (var prop in descriptor.globalProperties) - { - sb.Append($@" - {ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};"); - } - sb.AppendLine(@" + foreach (var prop in globalProperties) + { + sb.Append($@" +{ShaderPropertyTypeToHLSLType(prop.type)} {prop.name};"); + } + sb.AppendLine(@" }; #endif // GLOBALDATA_G_HLSL"); - globalFileStream.Write(sb.ToString()); - } + globalFileStream.Write(sb.ToString()); - // Compile each pass. - foreach (var pass in descriptor.passes) - { - GeneratePass(pass, targetDirectory); - } + return globalFilePath; } -} +} \ No newline at end of file diff --git a/Ghost.Shader/Generator/ShaderStructGenerator.cs b/Ghost.Shader/Generator/ShaderStructGenerator.cs index ae4bcf0..2e48b74 100644 --- a/Ghost.Shader/Generator/ShaderStructGenerator.cs +++ b/Ghost.Shader/Generator/ShaderStructGenerator.cs @@ -274,6 +274,7 @@ struct {structName} var sb = new StringBuilder(); sb.AppendLine(@$"// Auto-generated HLSL code, please do not edit this file directly. + #ifndef {hlslDefine} #define {hlslDefine}");