feat(shader): refactor and enhance shader compilation
Refactored shader compilation and resource management systems: - Introduced `DXCShaderCompiler` for HLSL compilation and reflection. - Added `BuildFinalShaderCode` method for robust shader code generation. - Replaced raw strings with `ShaderEntryPoint` struct for shader paths. - Updated `RenderContext` and `RenderGraphContext` for new pipeline methods. - Added thread-safe resource management methods in `ResourceManager`. - Introduced `DXCShaderReflectionData` for shader reflection handling. - Removed redundant code and simplified `ShaderPropertiesRegistry`. BREAKING CHANGE: Updated shader and resource APIs to use new structures and methods.
This commit is contained in:
36
src/Runtime/Ghost.Core/Graphics/GenerateHLSLAttribute.cs
Normal file
36
src/Runtime/Ghost.Core/Graphics/GenerateHLSLAttribute.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Struct)]
|
||||
public class GenerateShaderPropertyAttribute : Attribute
|
||||
{
|
||||
public GenerateShaderPropertyAttribute(string shaderName, string? name = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class GenerateAsHLSLTypeAttribute : Attribute
|
||||
{
|
||||
public GenerateAsHLSLTypeAttribute(string hlslTypeName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public enum PackingRules
|
||||
{
|
||||
Exact,
|
||||
Aligned,
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Enum)]
|
||||
public class GenerateHLSLAttribute : Attribute
|
||||
{
|
||||
private readonly PackingRules _packingRules;
|
||||
private readonly string? _outputSource;
|
||||
|
||||
public GenerateHLSLAttribute(PackingRules packingRules, string? outputSource)
|
||||
{
|
||||
_packingRules = packingRules;
|
||||
_outputSource = outputSource;
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@ public enum KeywordSpace
|
||||
Global,
|
||||
}
|
||||
|
||||
public struct ShaderEntryPoint
|
||||
public struct ShaderCode
|
||||
{
|
||||
public string entry;
|
||||
public string shader;
|
||||
public string code;
|
||||
public string entryPoint;
|
||||
|
||||
public readonly bool IsCreated => !string.IsNullOrEmpty(entry) && !string.IsNullOrEmpty(shader);
|
||||
public readonly bool IsCreated => !string.IsNullOrEmpty(code) && !string.IsNullOrEmpty(entryPoint);
|
||||
}
|
||||
|
||||
public struct KeywordsGroup
|
||||
@@ -35,35 +35,29 @@ public struct PassDescriptor
|
||||
public ulong identifier;
|
||||
public string name;
|
||||
|
||||
public string? hlsl;
|
||||
public ShaderEntryPoint taskShader;
|
||||
public ShaderEntryPoint meshShader;
|
||||
public ShaderEntryPoint pixelShader;
|
||||
public ShaderCode amplificationShaderCode;
|
||||
public ShaderCode meshShaderCode;
|
||||
public ShaderCode pixelShaderCode;
|
||||
public string[] defines;
|
||||
public string[] includes;
|
||||
public KeywordsGroup[] keywords;
|
||||
public PipelineState localPipeline;
|
||||
}
|
||||
|
||||
public class GraphicsShaderDescriptor
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public string propertiesCode = string.Empty;
|
||||
public uint propertyBufferSize;
|
||||
public ShaderModel shaderModel;
|
||||
public PassDescriptor[] passes = Array.Empty<PassDescriptor>();
|
||||
public required string name = string.Empty;
|
||||
public required uint propertyBufferSize;
|
||||
public required ShaderModel shaderModel;
|
||||
public required PassDescriptor[] passes = Array.Empty<PassDescriptor>();
|
||||
}
|
||||
|
||||
public class ComputeShaderDescriptor
|
||||
{
|
||||
public ulong identifier;
|
||||
public string name = string.Empty;
|
||||
public string propertiesCode = string.Empty;
|
||||
public uint propertyBufferSize;
|
||||
public string? hlsl;
|
||||
public ShaderModel shaderModel;
|
||||
public string[] defines = Array.Empty<string>();
|
||||
public string[] includes = Array.Empty<string>();
|
||||
public KeywordsGroup[] keywords = Array.Empty<KeywordsGroup>();
|
||||
public ShaderEntryPoint[] entryPoints = Array.Empty<ShaderEntryPoint>();
|
||||
public required ulong identifier;
|
||||
public required string name = string.Empty;
|
||||
public required uint propertyBufferSize;
|
||||
public required ShaderModel shaderModel;
|
||||
public required ShaderCode[] shaderCodes;
|
||||
public required string[] defines;
|
||||
public required KeywordsGroup[] keywords;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Struct)]
|
||||
public class GenerateShaderPropertyAttribute : Attribute
|
||||
{
|
||||
public GenerateShaderPropertyAttribute(string shaderName, string? name = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class GenerateAsHLSLTypeAttribute : Attribute
|
||||
{
|
||||
public GenerateAsHLSLTypeAttribute(string hlslTypeName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
public struct ShaderPropertyInfo
|
||||
{
|
||||
public string shaderName;
|
||||
public string code;
|
||||
public uint size;
|
||||
}
|
||||
|
||||
public static class ShaderPropertiesRegistry
|
||||
{
|
||||
private static readonly Dictionary<string, ShaderPropertyInfo> s_nameToCode = new Dictionary<string, ShaderPropertyInfo>(StringComparer.Ordinal);
|
||||
|
||||
public static void Register(string name, string code, uint size)
|
||||
{
|
||||
s_nameToCode[name] = new ShaderPropertyInfo { shaderName = name, code = code, size = size };
|
||||
}
|
||||
|
||||
public static bool TryGetInfo(string name, out ShaderPropertyInfo info)
|
||||
{
|
||||
return s_nameToCode.TryGetValue(name, out info);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -433,4 +433,41 @@ public static class ResultExtensions
|
||||
|
||||
return func(result.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Match(this Result result, Action onSuccess, Action<string?> onFailure)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
onSuccess();
|
||||
}
|
||||
else
|
||||
{
|
||||
onFailure(result.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static U Match<T, U>(this Result<T> result, Func<T, U> onSuccess, Func<string?, U> onFailure)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return onSuccess(result.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return onFailure(result.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public static U Match<T, U, E>(this Result<T, E> result, Func<T, U> onSuccess, Func<E, U> onFailure)
|
||||
where E : struct, Enum
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return onSuccess(result.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return onFailure(result.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Ghost.Engine;
|
||||
|
||||
public enum ShadowCastingMode
|
||||
public enum ShadowCastingMode : uint
|
||||
{
|
||||
Off,
|
||||
On,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Entities;
|
||||
using Ghost.Graphics;
|
||||
using Misaki.HighPerformance.Jobs;
|
||||
|
||||
@@ -1,12 +1,35 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Engine.RenderPipeline;
|
||||
|
||||
internal class GhostRenderPipeline : IRenderPipeline
|
||||
{
|
||||
private struct AddInstanceData
|
||||
{
|
||||
public float4x4 localToWorld;
|
||||
public uint instanceId;
|
||||
public uint meshBuffer;
|
||||
public uint materialPalette;
|
||||
public uint renderingLayerMask;
|
||||
public uint shadowCastingMode;
|
||||
}
|
||||
|
||||
private struct RemoveInstanceData
|
||||
{
|
||||
public uint instanceId;
|
||||
public uint swapWithInstanceId;
|
||||
}
|
||||
|
||||
private readonly RenderSystem _renderSystem;
|
||||
|
||||
private readonly RenderGraph _renderGraph;
|
||||
private readonly GPUScene _gpuScene;
|
||||
|
||||
public GPUScene GPUScene => _gpuScene;
|
||||
@@ -14,9 +37,90 @@ internal class GhostRenderPipeline : IRenderPipeline
|
||||
public GhostRenderPipeline(RenderSystem renderSystem)
|
||||
{
|
||||
_renderSystem = renderSystem;
|
||||
|
||||
_renderGraph = new RenderGraph(renderSystem.ResourceManager, renderSystem.GraphicsEngine);
|
||||
_gpuScene = new GPUScene(renderSystem.GraphicsEngine.ResourceAllocator, renderSystem.GraphicsEngine.ResourceDatabase, 102_400u); // 102.4k objects should be enough for now
|
||||
}
|
||||
|
||||
private static unsafe Handle<GPUBuffer> CreateAddInstanceBuffer(GhostRenderPayload ghostPayload, ResourceManager resourceManager, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
if (!ghostPayload.AddRequest.IsEmpty)
|
||||
{
|
||||
var addDesc = new BufferDesc
|
||||
{
|
||||
Size = (nuint)ghostPayload.AddRequest.Count * MemoryUtility.SizeOf<AddInstanceData>(),
|
||||
Stride = (uint)MemoryUtility.SizeOf<AddInstanceData>(),
|
||||
Usage = BufferUsage.Structured | BufferUsage.ShaderResource,
|
||||
HeapType = HeapType.Upload
|
||||
};
|
||||
|
||||
var addBuffer = resourceManager.CreateTransientBuffer(in addDesc, "Add Instance Buffer");
|
||||
var pAddData = (AddInstanceData*)resourceDatabase.MapResource(addBuffer.AsResource(), 0, null);
|
||||
|
||||
var i = 0;
|
||||
while (ghostPayload.AddRequest.TryDequeue(out var addRequest))
|
||||
{
|
||||
var (mesh, error) = resourceManager.GetMeshReference(addRequest.meshInstance.mesh);
|
||||
if (error.IsFailure)
|
||||
{
|
||||
Debug.Fail($"Failed to get mesh reference for mesh instance with ID {addRequest.instanceId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
pAddData[i] = new AddInstanceData
|
||||
{
|
||||
localToWorld = addRequest.localToWorld,
|
||||
instanceId = addRequest.instanceId,
|
||||
meshBuffer = resourceDatabase.GetBindlessIndex(mesh.Get().MeshDataBuffer.AsResource()),
|
||||
materialPalette = (uint)addRequest.meshInstance.materialPalette.Value,
|
||||
renderingLayerMask = addRequest.meshInstance.renderingLayerMask,
|
||||
shadowCastingMode = (uint)addRequest.meshInstance.shadowCastingMode
|
||||
};
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
resourceDatabase.UnmapResource(addBuffer.AsResource(), 0, null);
|
||||
return addBuffer;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private static unsafe Handle<GPUBuffer> CreateRemoveInstanceBuffer(GhostRenderPayload ghostPayload, ResourceManager resourceManager, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
if (!ghostPayload.RemoveRequest.IsEmpty)
|
||||
{
|
||||
var addDesc = new BufferDesc
|
||||
{
|
||||
Size = (nuint)ghostPayload.AddRequest.Count * MemoryUtility.SizeOf<RemoveInstanceData>(),
|
||||
Stride = (uint)MemoryUtility.SizeOf<RemoveInstanceData>(),
|
||||
Usage = BufferUsage.Structured | BufferUsage.ShaderResource,
|
||||
HeapType = HeapType.Upload
|
||||
};
|
||||
|
||||
var removeBuffer = resourceManager.CreateTransientBuffer(in addDesc, "Remove Instance Buffer");
|
||||
var pRemoveData = (RemoveInstanceData*)resourceDatabase.MapResource(removeBuffer.AsResource(), 0, null);
|
||||
|
||||
var i = 0;
|
||||
while (ghostPayload.RemoveRequest.TryDequeue(out var removeRequest))
|
||||
{
|
||||
pRemoveData[i] = new RemoveInstanceData
|
||||
{
|
||||
instanceId = removeRequest.instanceId,
|
||||
swapWithInstanceId = removeRequest.swapWithInstanceId
|
||||
};
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
resourceDatabase.UnmapResource(removeBuffer.AsResource(), 0, null);
|
||||
return removeBuffer;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Render(RenderContext ctx, int frameIndex, IRenderPayload payload)
|
||||
{
|
||||
var ghostPayload = (GhostRenderPayload)payload;
|
||||
@@ -26,15 +130,21 @@ internal class GhostRenderPipeline : IRenderPipeline
|
||||
|
||||
foreach (ref readonly var request in ghostPayload.RenderRequests)
|
||||
{
|
||||
if (!RenderPipelineUtility.GetViewAndProjectionMatrices(_renderSystem, in request, out var view, out var projection, out var screenSize))
|
||||
if (!RenderPipelineUtility.GetVPMatrices(_renderSystem, in request, out var view, out var projection, out var screenSize))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var addBuffer = CreateAddInstanceBuffer(ghostPayload, resourceManager, resourceDatabase);
|
||||
var removeBuffer = CreateRemoveInstanceBuffer(ghostPayload, resourceManager, resourceDatabase);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
_renderGraph.Dispose();
|
||||
_gpuScene.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ struct {info.Name}
|
||||
codeBuilder.Clear();
|
||||
|
||||
var typeFullName = info.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
registerBuilder.AppendLine($@" global::Ghost.Core.Graphics.ShaderPropertiesRegistry.Register(""{info.ShaderName}"", {typeFullName}.HLSL_SOURCE, (uint)sizeof({typeFullName}));");
|
||||
registerBuilder.AppendLine($@" global::Ghost.DSL.ShaderPropertiesRegistry.Register(""{info.ShaderName}"", {typeFullName}.HLSL_SOURCE, (uint)sizeof({typeFullName}));");
|
||||
}
|
||||
|
||||
var registerTypeName = "g_shaderproperty_registeration";
|
||||
|
||||
@@ -3,44 +3,15 @@
|
||||
#endif
|
||||
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
public static class D3D12GraphicsEngineFactory
|
||||
{
|
||||
static D3D12GraphicsEngineFactory()
|
||||
{
|
||||
var currentDir = AppContext.BaseDirectory;
|
||||
var platform = OperatingSystem.IsWindows() ? "win" :
|
||||
OperatingSystem.IsLinux() ? "linux" :
|
||||
OperatingSystem.IsMacOS() ? "osx" : "unknown";
|
||||
var arch = Environment.Is64BitProcess ? "x64" : "x86";
|
||||
var nativeDllDir = Path.Combine(currentDir, "runtimes", platform + "-" + arch, "native");
|
||||
|
||||
AssemblyLoadContext.Default.ResolvingUnmanagedDll += (assembly, libraryName) =>
|
||||
{
|
||||
if (libraryName == "dxcompiler")
|
||||
{
|
||||
NativeLibrary.TryLoad(Path.Combine(nativeDllDir, "dxil.dll"), out _);
|
||||
|
||||
if (NativeLibrary.TryLoad(Path.Combine(nativeDllDir, "dxcompiler.dll"), out var dxcHandle))
|
||||
{
|
||||
return dxcHandle;
|
||||
}
|
||||
}
|
||||
|
||||
return IntPtr.Zero;
|
||||
};
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IGraphicsEngine Create(GraphicsEngineDesc desc)
|
||||
{
|
||||
@@ -68,7 +39,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
private readonly D3D12DebugLayer _debugLayer;
|
||||
#endif
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly DXCShaderCompiler _shaderCompiler;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
@@ -81,7 +51,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
private bool _disposed;
|
||||
|
||||
public IRenderDevice Device => _device;
|
||||
public IShaderCompiler ShaderCompiler => _shaderCompiler;
|
||||
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||
@@ -94,7 +63,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
_debugLayer = new D3D12DebugLayer();
|
||||
#endif
|
||||
_device = new D3D12RenderDevice();
|
||||
_shaderCompiler = new DXCShaderCompiler();
|
||||
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
||||
|
||||
_resourceDatabase = new D3D12ResourceDatabase(_descriptorAllocator);
|
||||
@@ -133,7 +101,7 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
for (int i = 0; i < _commandBufferPool.Count; i++)
|
||||
for (var i = 0; i < _commandBufferPool.Count; i++)
|
||||
{
|
||||
if (_commandBufferPool[i].Type == type)
|
||||
{
|
||||
@@ -209,7 +177,6 @@ internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
_resourceDatabase.Dispose();
|
||||
|
||||
_descriptorAllocator.Dispose();
|
||||
_shaderCompiler.Dispose();
|
||||
_device.Dispose();
|
||||
#if ENABLE_DEBUG_LAYER
|
||||
_debugLayer.Dispose();
|
||||
|
||||
@@ -28,7 +28,6 @@ internal struct D3D12PipelineState : IDisposable
|
||||
internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>, IPipelineLibrary
|
||||
{
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
@@ -56,11 +55,10 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
return pLibrary;
|
||||
}
|
||||
|
||||
public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
|
||||
public D3D12PipelineLibrary(D3D12RenderDevice device)
|
||||
: base(CreateLibrary(device, null)) // TODO: we need to path to load the existing library from disk.
|
||||
{
|
||||
_device = device;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_pipelineCache = new UnsafeHashMap<UInt128, D3D12PipelineState>(32, Allocator.Persistent);
|
||||
|
||||
@@ -82,7 +80,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
{
|
||||
ShaderRegister = 0, // b0
|
||||
RegisterSpace = 0, // space0
|
||||
Num32BitValues = PushConstantsData.NUM_32BITS_VALUE
|
||||
Num32BitValues = PushConstantsData.NUM_32BITS_VALUE // 3
|
||||
}
|
||||
};
|
||||
|
||||
@@ -139,32 +137,6 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
fs.Write(buffer.AsSpan());
|
||||
}
|
||||
|
||||
private static Result ValidateReflectionData(ShaderReflectionData reflectionData)
|
||||
{
|
||||
if (reflectionData.ResourcesBindings.Count > RootSignatureLayout.ROOT_PARAMETER_COUNT)
|
||||
{
|
||||
return Result.Failure($"Shader uses more root parameters than supported ({RootSignatureLayout.ROOT_PARAMETER_COUNT}).");
|
||||
}
|
||||
|
||||
if (reflectionData.ResourcesBindings.Count == 0)
|
||||
{
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
var rootConstant = reflectionData.ResourcesBindings[0];
|
||||
if (rootConstant.Type != ShaderInputType.ConstantBuffer)
|
||||
{
|
||||
return Result.Failure($"Root constant parameter must be a constant buffer.");
|
||||
}
|
||||
|
||||
if (rootConstant.Size != sizeof(PushConstantsData))
|
||||
{
|
||||
return Result.Failure($"Root constant buffer size must be {sizeof(PushConstantsData)} bytes.");
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTest ztest, ZWrite zwrite)
|
||||
{
|
||||
var depthEnabled = ztest != ZTest.Disabled;
|
||||
@@ -185,6 +157,7 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
}
|
||||
|
||||
var hr = pNativeObject->LoadPipeline(pKeyStr, pStreamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||
|
||||
if (hr == E.E_INVALIDARG)
|
||||
{
|
||||
// Pipeline not found in the library, create a new one.
|
||||
@@ -204,34 +177,8 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public Result<Key128<GraphicsPipeline>> CreateGraphicsPipeline(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled)
|
||||
public Result<Key128<GraphicsPipeline>> CreateGraphicsPipeline(ref readonly GraphicsPSODescriptor descriptor, ReadOnlySpan<byte> asByteCode, ReadOnlySpan<byte> msByteCode, ReadOnlySpan<byte> psByteCode)
|
||||
{
|
||||
static Result ValidatePassReflectionData(ref readonly GraphicsCompiledResult compiled)
|
||||
{
|
||||
var msr = ValidateReflectionData(compiled.msResult.reflectionData);
|
||||
if (msr.IsFailure)
|
||||
{
|
||||
return Result.Failure("Validation of mesh shader reflection data failed: " + msr.Message);
|
||||
}
|
||||
|
||||
var psr = ValidateReflectionData(compiled.psResult.reflectionData);
|
||||
if (psr.IsFailure)
|
||||
{
|
||||
return Result.Failure("Validation of pixel shader reflection data failed: " + psr.Message);
|
||||
}
|
||||
|
||||
if (compiled.tsResult.IsCreated)
|
||||
{
|
||||
var tsr = ValidateReflectionData(compiled.tsResult.reflectionData);
|
||||
if (tsr.IsFailure)
|
||||
{
|
||||
return Result.Failure("Validation of task shader reflection data failed: " + tsr.Message);
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
AssertNotDisposed();
|
||||
|
||||
if (descriptor.RtvFormats.Length > D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT)
|
||||
@@ -244,98 +191,92 @@ internal unsafe class D3D12PipelineLibrary : D3D12Object<ID3D12PipelineLibrary1>
|
||||
|
||||
if (!_pipelineCache.ContainsKey(pipelineKey))
|
||||
{
|
||||
var result = ValidatePassReflectionData(in compiled);
|
||||
if (result.IsFailure)
|
||||
fixed (byte* pASByteCode = asByteCode, pMSByteCode = msByteCode, pPSByteCode = psByteCode)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
|
||||
{
|
||||
pRootSignature = _defaultRootSignature.Get(),
|
||||
MS = new D3D12_SHADER_BYTECODE(compiled.msResult.bytecode.GetUnsafePtr(), (nuint)compiled.msResult.bytecode.Count),
|
||||
PS = new D3D12_SHADER_BYTECODE(compiled.psResult.bytecode.GetUnsafePtr(), (nuint)compiled.psResult.bytecode.Count),
|
||||
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||
SampleMask = UINT32_MAX,
|
||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||
NumRenderTargets = (uint)descriptor.RtvFormats.Length,
|
||||
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
|
||||
DepthStencilState = BuildDepthStencil(descriptor.PipelineOption.ZTest, descriptor.PipelineOption.ZWrite),
|
||||
NodeMask = 0,
|
||||
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
||||
|
||||
BlendState = descriptor.PipelineOption.Blend switch
|
||||
var desc = new D3DX12_MESH_SHADER_PIPELINE_STATE_DESC
|
||||
{
|
||||
Blend.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
|
||||
Blend.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
|
||||
Blend.Additive => D3D12Utility.D3D12_BLEND_DESC_ADDITIVE,
|
||||
Blend.Multiply => D3D12Utility.D3D12_BLEND_DESC_MULTIPLY,
|
||||
Blend.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
|
||||
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
|
||||
},
|
||||
RasterizerState = descriptor.PipelineOption.Cull switch
|
||||
pRootSignature = _defaultRootSignature.Get(),
|
||||
MS = new D3D12_SHADER_BYTECODE(pMSByteCode, (nuint)msByteCode.Length),
|
||||
PS = new D3D12_SHADER_BYTECODE(pPSByteCode, (nuint)psByteCode.Length),
|
||||
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||
SampleMask = UINT32_MAX,
|
||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||
NumRenderTargets = (uint)descriptor.RtvFormats.Length,
|
||||
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
|
||||
DepthStencilState = BuildDepthStencil(descriptor.PipelineOption.ZTest, descriptor.PipelineOption.ZWrite),
|
||||
NodeMask = 0,
|
||||
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
||||
|
||||
BlendState = descriptor.PipelineOption.Blend switch
|
||||
{
|
||||
Blend.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
|
||||
Blend.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
|
||||
Blend.Additive => D3D12Utility.D3D12_BLEND_DESC_ADDITIVE,
|
||||
Blend.Multiply => D3D12Utility.D3D12_BLEND_DESC_MULTIPLY,
|
||||
Blend.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
|
||||
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
|
||||
},
|
||||
RasterizerState = descriptor.PipelineOption.Cull switch
|
||||
{
|
||||
Cull.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
|
||||
Cull.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
|
||||
Cull.Back => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE,
|
||||
_ => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE
|
||||
},
|
||||
};
|
||||
|
||||
if (asByteCode.Length != 0)
|
||||
{
|
||||
Cull.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
|
||||
Cull.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
|
||||
Cull.Back => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE,
|
||||
_ => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE
|
||||
},
|
||||
};
|
||||
desc.AS = new D3D12_SHADER_BYTECODE(pASByteCode, (nuint)asByteCode.Length);
|
||||
}
|
||||
|
||||
if (compiled.tsResult.IsCreated)
|
||||
{
|
||||
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
||||
}
|
||||
for (var i = 0; i < descriptor.RtvFormats.Length; i++)
|
||||
{
|
||||
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
||||
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)((int)descriptor.PipelineOption.ColorMask & 0x0F);
|
||||
}
|
||||
|
||||
for (var i = 0; i < descriptor.RtvFormats.Length; i++)
|
||||
{
|
||||
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
||||
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)((int)descriptor.PipelineOption.ColorMask & 0x0F);
|
||||
}
|
||||
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
pPipelineStateSubobjectStream = &meshStream,
|
||||
SizeInBytes = (nuint)sizeof(CD3DX12_PIPELINE_MESH_STATE_STREAM)
|
||||
};
|
||||
|
||||
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
pPipelineStateSubobjectStream = &meshStream,
|
||||
SizeInBytes = (nuint)sizeof(CD3DX12_PIPELINE_MESH_STATE_STREAM)
|
||||
};
|
||||
|
||||
result = CreatePSO(descriptor.VariantKey, pipelineKey, &streamDesc);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
var result = CreatePSO(descriptor.VariantKey, pipelineKey, &streamDesc);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pipelineKey;
|
||||
}
|
||||
|
||||
public Result<Key128<ComputePipeline>> CreateComputePipeline(ref readonly ComputePSODescriptor descriptor, ref readonly ShaderCompileResult compiled)
|
||||
public Result<Key128<ComputePipeline>> CreateComputePipeline(ref readonly ComputePSODescriptor descriptor, ReadOnlySpan<byte> csBytecode)
|
||||
{
|
||||
AssertNotDisposed();
|
||||
|
||||
var pipelineKey = RHIUtility.CreateComputePipelineKey(descriptor.VariantKey, compiled.hashCode);
|
||||
var pipelineKey = RHIUtility.CreateComputePipelineKey(descriptor.VariantKey);
|
||||
if (!_pipelineCache.ContainsKey(pipelineKey))
|
||||
{
|
||||
var result = ValidateReflectionData(compiled.reflectionData);
|
||||
if (result.IsFailure)
|
||||
fixed (byte* pCSByteCode = csBytecode)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
var byteCode = new D3D12_SHADER_BYTECODE(pCSByteCode, (nuint)csBytecode.Length);
|
||||
var desc = new CD3DX12_PIPELINE_STATE_STREAM_CS(in byteCode);
|
||||
|
||||
var byteCode = new D3D12_SHADER_BYTECODE(compiled.bytecode.GetUnsafePtr(), (nuint)compiled.bytecode.Length);
|
||||
var desc = new CD3DX12_PIPELINE_STATE_STREAM_CS(in byteCode);
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
pPipelineStateSubobjectStream = &desc,
|
||||
SizeInBytes = (nuint)sizeof(CD3DX12_PIPELINE_STATE_STREAM_CS)
|
||||
};
|
||||
|
||||
var streamDesc = new D3D12_PIPELINE_STATE_STREAM_DESC
|
||||
{
|
||||
pPipelineStateSubobjectStream = &desc,
|
||||
SizeInBytes = (nuint)sizeof(CD3DX12_PIPELINE_STATE_STREAM_CS)
|
||||
};
|
||||
|
||||
result = CreatePSO(descriptor.VariantKey, pipelineKey, &streamDesc);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
var result = CreatePSO(descriptor.VariantKey, pipelineKey, &streamDesc);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.DirectX.DXC;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
internal sealed partial class DXCShaderCompiler
|
||||
{
|
||||
private static string GetProfileString(ShaderStage stage, ShaderModel version)
|
||||
{
|
||||
return (stage, version) switch
|
||||
{
|
||||
(ShaderStage.TaskShader, ShaderModel.SM_6_6) => "as_6_6",
|
||||
(ShaderStage.PixelShader, ShaderModel.SM_6_6) => "ps_6_6",
|
||||
(ShaderStage.MeshShader, ShaderModel.SM_6_6) => "ms_6_6",
|
||||
(ShaderStage.ComputeShader, ShaderModel.SM_6_6) => "cs_6_6",
|
||||
(ShaderStage.TaskShader, ShaderModel.SM_6_7) => "as_6_7",
|
||||
(ShaderStage.PixelShader, ShaderModel.SM_6_7) => "ps_6_7",
|
||||
(ShaderStage.MeshShader, ShaderModel.SM_6_7) => "ms_6_7",
|
||||
(ShaderStage.ComputeShader, ShaderModel.SM_6_7) => "cs_6_7",
|
||||
(ShaderStage.TaskShader, ShaderModel.SM_6_8) => "as_6_8",
|
||||
(ShaderStage.PixelShader, ShaderModel.SM_6_8) => "ps_6_8",
|
||||
(ShaderStage.MeshShader, ShaderModel.SM_6_8) => "ms_6_8",
|
||||
(ShaderStage.ComputeShader, ShaderModel.SM_6_8) => "cs_6_8",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(stage), "Unsupported shader stage or compiler version")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetOptimizeLevelString(CompilerOptimizeLevel level)
|
||||
{
|
||||
return level switch
|
||||
{
|
||||
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")
|
||||
};
|
||||
}
|
||||
|
||||
private static List<string> GetCompilerArguments(ref readonly ShaderCompilationConfig config)
|
||||
{
|
||||
var argsArray = new List<string>
|
||||
{
|
||||
"-T", GetProfileString(config.stage, config.model), // Target profile (ms_6_6, ps_6_6)
|
||||
"-E", config.entryPoint, // Entry point
|
||||
"-HV", "2021", // HLSL version 2021
|
||||
"-enable-16bit-types", // Enable 16-bit types
|
||||
GetOptimizeLevelString(config.optimizeLevel), // Optimization level
|
||||
};
|
||||
|
||||
foreach (var define in config.defines)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add(define);
|
||||
}
|
||||
|
||||
if (config.stage == ShaderStage.TaskShader
|
||||
|| config.stage == ShaderStage.MeshShader
|
||||
|| config.stage == ShaderStage.PixelShader)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add("__GRAPHICS__");
|
||||
}
|
||||
else if (config.stage == ShaderStage.ComputeShader)
|
||||
{
|
||||
argsArray.Add("-D");
|
||||
argsArray.Add("__COMPUTE__");
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepDebugInfo))
|
||||
{
|
||||
argsArray.Add("-Qstrip_debug");
|
||||
}
|
||||
|
||||
if (!config.options.HasFlag(CompilerOption.KeepReflections))
|
||||
{
|
||||
argsArray.Add("-Qstrip_reflect");
|
||||
}
|
||||
|
||||
if (config.options.HasFlag(CompilerOption.WarnAsError))
|
||||
{
|
||||
argsArray.Add(DXC_ARG_WARNINGS_ARE_ERRORS);
|
||||
}
|
||||
|
||||
if (config.options.HasFlag(CompilerOption.SpirvCrossCompile))
|
||||
{
|
||||
argsArray.Add("-spirv");
|
||||
}
|
||||
|
||||
return argsArray;
|
||||
}
|
||||
|
||||
private static Result<string, Error> BuildFinalShaderCode(string shaderPath, ReadOnlySpan<string> includes, string? injectedCode)
|
||||
{
|
||||
string shaderCode;
|
||||
if (shaderPath == "hlsl_block")
|
||||
{
|
||||
if (string.IsNullOrEmpty(injectedCode))
|
||||
{
|
||||
return Error.InvalidArgument;
|
||||
}
|
||||
|
||||
shaderCode = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!File.Exists(shaderPath))
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
shaderCode = File.ReadAllText(shaderPath);
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var includePath in includes)
|
||||
{
|
||||
sb.AppendLine($"#include \"{includePath}\"");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(injectedCode))
|
||||
{
|
||||
sb.AppendLine($"#line 0 \"injected_code\"");
|
||||
sb.AppendLine(injectedCode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(shaderCode))
|
||||
{
|
||||
sb.AppendLine($"#line 0 \"{shaderPath}\"");
|
||||
sb.AppendLine(shaderCode);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static ShaderInputType ToInputType(D3D_SHADER_INPUT_TYPE type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER => ShaderInputType.ConstantBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_TBUFFER => ShaderInputType.Texture,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_TEXTURE => ShaderInputType.Texture,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_SAMPLER => ShaderInputType.Sampler,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWTYPED => ShaderInputType.UAV,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_STRUCTURED => ShaderInputType.StructuredBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_BYTEADDRESS => ShaderInputType.ByteAddressBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWSTRUCTURED => ShaderInputType.RWStructuredBuffer,
|
||||
D3D_SHADER_INPUT_TYPE.D3D_SIT_UAV_RWBYTEADDRESS => ShaderInputType.RWByteAddressBuffer,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), "Unsupported shader input type")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed unsafe partial class DXCShaderCompiler : IShaderCompiler
|
||||
{
|
||||
private UniquePtr<IDxcCompiler3> _compiler;
|
||||
private UniquePtr<IDxcUtils> _utils;
|
||||
// NOTE: This is just a temporary cache for compiled shader code. We will implement a proper disk cache later.
|
||||
// TODO: This should be shader variant specific cache instead of pass specific.
|
||||
private readonly Dictionary<ulong, GraphicsCompiledResult> _compiledResults;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public DXCShaderCompiler()
|
||||
{
|
||||
// Initialize DXC _compiler.Get() and _utils.Get()
|
||||
var dxccID = CLSID.CLSID_DxcCompiler;
|
||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||
|
||||
IDxcCompiler3* pCompiler = default;
|
||||
IDxcUtils* pUtils = default;
|
||||
ThrowIfFailed(DxcCreateInstance(&dxccID, __uuidof(pCompiler), (void**)&pCompiler));
|
||||
ThrowIfFailed(DxcCreateInstance(&dxcuID, __uuidof(pUtils), (void**)&pUtils));
|
||||
|
||||
_compiler.Attach(pCompiler);
|
||||
_utils.Attach(pUtils);
|
||||
|
||||
_compiledResults = new Dictionary<ulong, GraphicsCompiledResult>();
|
||||
}
|
||||
|
||||
~DXCShaderCompiler()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private Result<ShaderReflectionData> PerformDXCReflection(IDxcBlob* pReflectionBlob)
|
||||
{
|
||||
ID3D12ShaderReflection* pReflection = default;
|
||||
|
||||
try
|
||||
{
|
||||
// Create DXC _utils.Get() to parse reflection data
|
||||
var dxcuID = CLSID.CLSID_DxcUtils;
|
||||
|
||||
// Create reflection interface from blob
|
||||
var reflectionBuffer = new DxcBuffer
|
||||
{
|
||||
Ptr = pReflectionBlob->GetBufferPointer(),
|
||||
Size = pReflectionBlob->GetBufferSize(),
|
||||
Encoding = DXC_CP_ACP
|
||||
};
|
||||
|
||||
ThrowIfFailed(_utils.Get()->CreateReflection(&reflectionBuffer, __uuidof(pReflection), (void**)&pReflection));
|
||||
|
||||
D3D12_SHADER_DESC shaderDesc;
|
||||
ThrowIfFailed(pReflection->GetDesc(&shaderDesc));
|
||||
|
||||
var reflectionData = new ShaderReflectionData();
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
D3D12_SHADER_INPUT_BIND_DESC bindDesc;
|
||||
ThrowIfFailed(pReflection->GetResourceBindingDesc(i, &bindDesc));
|
||||
|
||||
var resourceName = Marshal.PtrToStringUTF8((IntPtr)bindDesc.Name);
|
||||
if (resourceName == null)
|
||||
{
|
||||
return Result.Failure("Failed to get resource name from reflection data.");
|
||||
}
|
||||
|
||||
var info = new ResourceBindingInfo
|
||||
{
|
||||
Name = resourceName,
|
||||
Type = ToInputType(bindDesc.Type),
|
||||
BindPoint = bindDesc.BindPoint,
|
||||
BindCount = bindDesc.BindCount,
|
||||
Space = bindDesc.Space
|
||||
};
|
||||
|
||||
switch (bindDesc.Type)
|
||||
{
|
||||
case D3D_SHADER_INPUT_TYPE.D3D_SIT_CBUFFER:
|
||||
{
|
||||
var cbuffer = pReflection->GetConstantBufferByName(bindDesc.Name);
|
||||
D3D12_SHADER_BUFFER_DESC cbufferDesc;
|
||||
ThrowIfFailed(cbuffer->GetDesc(&cbufferDesc));
|
||||
|
||||
var variables = new List<CBufferPropertyInfo>((int)cbufferDesc.Variables);
|
||||
|
||||
// Now we iterate all variables for *every* cbuffer, not just b3
|
||||
for (uint j = 0; j < cbufferDesc.Variables; j++)
|
||||
{
|
||||
var variable = cbuffer->GetVariableByIndex(j);
|
||||
D3D12_SHADER_VARIABLE_DESC varDesc;
|
||||
variable->GetDesc(&varDesc);
|
||||
|
||||
var variableName = Marshal.PtrToStringUTF8((IntPtr)varDesc.Name);
|
||||
if (variableName == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
variables.Add(new CBufferPropertyInfo
|
||||
{
|
||||
Name = variableName,
|
||||
StartOffset = varDesc.StartOffset,
|
||||
Size = varDesc.Size
|
||||
});
|
||||
}
|
||||
|
||||
info.Size = cbufferDesc.Size;
|
||||
info.Properties = variables;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: Currently we do not support resource bindings yet, everything access through bindless heaps.
|
||||
}
|
||||
|
||||
reflectionData.ResourcesBindings.Add(info);
|
||||
}
|
||||
|
||||
return reflectionData;
|
||||
}
|
||||
finally
|
||||
{
|
||||
pReflection->Release();
|
||||
}
|
||||
}
|
||||
|
||||
public Result<ShaderCompileResult> Compile(ref readonly ShaderCompilationConfig config, Allocator allocator)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
using ComPtr<IDxcIncludeHandler> includeHandler = default;
|
||||
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
|
||||
|
||||
ThrowIfFailed(_utils.Get()->CreateDefaultIncludeHandler(includeHandler.GetAddressOf()));
|
||||
|
||||
var finalShaderCodeResult = BuildFinalShaderCode(config.shaderPath, config.includes, config.injectedCode);
|
||||
if (finalShaderCodeResult.IsFailure)
|
||||
{
|
||||
return Result.Failure(finalShaderCodeResult.Error);
|
||||
}
|
||||
|
||||
var finalShaderCode = finalShaderCodeResult.Value;
|
||||
fixed (byte* pCode = System.Text.Encoding.UTF8.GetBytes(finalShaderCode))
|
||||
{
|
||||
var sizeInBytes = System.Text.Encoding.UTF8.GetByteCount(finalShaderCode);
|
||||
ThrowIfFailed(_utils.Get()->CreateBlobFromPinned(pCode, (uint)sizeInBytes, DXC_CP_UTF8, sourceBlob.GetAddressOf()));
|
||||
}
|
||||
|
||||
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<IDxcResult> result = default;
|
||||
|
||||
try
|
||||
{
|
||||
// Compile shader
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
Ptr = sourceBlob.Get()->GetBufferPointer(),
|
||||
Size = sourceBlob.Get()->GetBufferSize(),
|
||||
Encoding = DXC_CP_UTF8
|
||||
};
|
||||
|
||||
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler, __uuidof(result.Get()), (void**)&result));
|
||||
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
using ComPtr<IDxcBlob> bytecodeBlob = default;
|
||||
ThrowIfFailed(result.Get()->GetResult(bytecodeBlob.GetAddressOf()));
|
||||
|
||||
ShaderReflectionData reflectionData = default;
|
||||
if (config.options.HasFlag(CompilerOption.KeepReflections))
|
||||
{
|
||||
using ComPtr<IDxcBlob> reflection = default;
|
||||
if (result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, __uuidof(reflection.Get()), (void**)&reflection, null).SUCCEEDED)
|
||||
{
|
||||
reflectionData = PerformDXCReflection(reflection).GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
var bytecodeSize = bytecodeBlob.Get()->GetBufferSize();
|
||||
var bytecode = new UnsafeArray<byte>((int)bytecodeSize, allocator);
|
||||
|
||||
NativeMemory.Copy(bytecodeBlob.Get()->GetBufferPointer(), bytecode.GetUnsafePtr(), bytecodeSize);
|
||||
|
||||
return new ShaderCompileResult
|
||||
{
|
||||
bytecode = bytecode,
|
||||
reflectionData = reflectionData,
|
||||
hashCode = XxHash64.HashToUInt64(bytecode)
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
for (var i = 0; i < argsArray.Count; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal((nint)argPtrs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Result<GraphicsCompiledResult> CompilePass(ref readonly PassDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, ref readonly LocalKeywordSet keywords)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
string[] fullDefines;
|
||||
var totalDefineCount = descriptor.defines.Length + additionalConfig.defines.Length;
|
||||
if (totalDefineCount == 0)
|
||||
{
|
||||
fullDefines = Array.Empty<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
fullDefines = new string[totalDefineCount];
|
||||
descriptor.defines.CopyTo(fullDefines);
|
||||
additionalConfig.defines.CopyTo(fullDefines.AsSpan(descriptor.defines.Length));
|
||||
}
|
||||
|
||||
var injectedCodeBuilder = new StringBuilder();
|
||||
injectedCodeBuilder.AppendLine(descriptor.shader.propertiesCode);
|
||||
injectedCodeBuilder.AppendLine(descriptor.hlsl);
|
||||
injectedCodeBuilder.AppendLine(additionalConfig.injectedCode);
|
||||
|
||||
var injectedCode = injectedCodeBuilder.ToString();
|
||||
|
||||
ShaderCompileResult tsResult = default;
|
||||
var tsEntry = descriptor.taskShader;
|
||||
if (tsEntry.IsCreated)
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines,
|
||||
includes = descriptor.includes,
|
||||
shaderPath = tsEntry.shader,
|
||||
entryPoint = tsEntry.entry,
|
||||
injectedCode = injectedCode,
|
||||
stage = ShaderStage.TaskShader,
|
||||
model = additionalConfig.model,
|
||||
optimizeLevel = additionalConfig.optimizeLevel,
|
||||
options = additionalConfig.options,
|
||||
};
|
||||
|
||||
var result = Compile(ref config, Allocator.Persistent);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
tsResult = result.Value;
|
||||
}
|
||||
|
||||
ShaderCompileResult msResult;
|
||||
var msEntry = descriptor.meshShader;
|
||||
if (msEntry.IsCreated)
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines,
|
||||
includes = descriptor.includes,
|
||||
shaderPath = msEntry.shader,
|
||||
entryPoint = msEntry.entry,
|
||||
injectedCode = injectedCode,
|
||||
stage = ShaderStage.MeshShader,
|
||||
model = additionalConfig.model,
|
||||
optimizeLevel = additionalConfig.optimizeLevel,
|
||||
options = additionalConfig.options,
|
||||
};
|
||||
|
||||
var result = Compile(ref config, Allocator.Persistent);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
msResult = result.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result.Failure("Mesh shader expected.");
|
||||
}
|
||||
|
||||
ShaderCompileResult psResult;
|
||||
var psEntry = descriptor.pixelShader;
|
||||
if (psEntry.IsCreated)
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines,
|
||||
includes = descriptor.includes,
|
||||
shaderPath = psEntry.shader,
|
||||
entryPoint = psEntry.entry,
|
||||
injectedCode = injectedCode,
|
||||
stage = ShaderStage.PixelShader,
|
||||
model = additionalConfig.model,
|
||||
optimizeLevel = additionalConfig.optimizeLevel,
|
||||
options = additionalConfig.options,
|
||||
};
|
||||
|
||||
var result = Compile(ref config, Allocator.Persistent);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result.Failure(result.Message);
|
||||
}
|
||||
|
||||
psResult = result.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Result.Failure("Pixel shader expected.");
|
||||
}
|
||||
|
||||
var compiled = new GraphicsCompiledResult
|
||||
{
|
||||
tsResult = tsResult,
|
||||
msResult = msResult,
|
||||
psResult = psResult,
|
||||
};
|
||||
|
||||
var passHash = RHIUtility.CreateShaderPassKey(descriptor.identifier, compiled.HashCode);
|
||||
var variantHash = RHIUtility.CreateShaderVariantKey(passHash, in keywords);
|
||||
|
||||
_compiledResults[variantHash] = compiled;
|
||||
return compiled;
|
||||
}
|
||||
|
||||
public Result<GraphicsCompiledResult, Error> LoadCompiledCache(Key64<ShaderVariant> key)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_compiledResults.TryGetValue(key, out var compiledResult))
|
||||
{
|
||||
return compiledResult;
|
||||
}
|
||||
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var kvp in _compiledResults)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_compiler.Dispose();
|
||||
_utils.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,6 @@
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\win-x64\native\dxcompiler.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="runtimes\win-x64\native\dxil.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -17,11 +17,6 @@ public interface IGraphicsEngine : IDisposable
|
||||
get;
|
||||
}
|
||||
|
||||
IShaderCompiler ShaderCompiler
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IPipelineLibrary PipelineLibrary
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -6,6 +6,6 @@ public interface IPipelineLibrary : IDisposable
|
||||
{
|
||||
void SaveLibraryToDisk(string filePath);
|
||||
bool HasPipelineStateObject(UInt128 key);
|
||||
Result<Key128<GraphicsPipeline>> CreateGraphicsPipeline(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||
Result<Key128<ComputePipeline>> CreateComputePipeline(ref readonly ComputePSODescriptor descriptor, ref readonly ShaderCompileResult compiled);
|
||||
Result<Key128<GraphicsPipeline>> CreateGraphicsPipeline(ref readonly GraphicsPSODescriptor descriptor, ReadOnlySpan<byte> asByteCode, ReadOnlySpan<byte> msByteCode, ReadOnlySpan<byte> psByteCode);
|
||||
Result<Key128<ComputePipeline>> CreateComputePipeline(ref readonly ComputePSODescriptor descriptor, ReadOnlySpan<byte> csByteCode);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
@@ -9,7 +8,6 @@ namespace Ghost.Graphics.RHI;
|
||||
public struct ShaderCompileResult : IDisposable
|
||||
{
|
||||
public UnsafeArray<byte> bytecode;
|
||||
public ShaderReflectionData reflectionData;
|
||||
public ulong hashCode;
|
||||
|
||||
public readonly bool IsCreated => bytecode.IsCreated;
|
||||
@@ -20,42 +18,36 @@ public struct ShaderCompileResult : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public struct GraphicsCompiledResult : IDisposable
|
||||
public struct GraphicsCompiledResult
|
||||
{
|
||||
private ulong _hashCode;
|
||||
public ulong tsResultHash;
|
||||
public ulong msResultHash;
|
||||
public ulong psResultHash;
|
||||
|
||||
public ShaderCompileResult tsResult;
|
||||
public ShaderCompileResult msResult;
|
||||
public ShaderCompileResult psResult;
|
||||
public readonly ulong HashCode => Hash.Combine64(tsResultHash, msResultHash, psResultHash);
|
||||
}
|
||||
|
||||
public Key64<GraphicsCompiledResult> HashCode
|
||||
public unsafe struct ComputeCompileResult
|
||||
{
|
||||
public fixed ulong resultHash[8];
|
||||
public readonly int count;
|
||||
|
||||
public ulong HashCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hashCode == 0)
|
||||
{
|
||||
_hashCode = Hash.Combine64(tsResult.hashCode, msResult.hashCode, psResult.hashCode);
|
||||
}
|
||||
|
||||
return _hashCode;
|
||||
var a = Hash.Combine64(resultHash[0], resultHash[1], resultHash[2], resultHash[3]);
|
||||
var b = Hash.Combine64(resultHash[4], resultHash[5], resultHash[6], resultHash[7]);
|
||||
return Hash.Combine64(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
tsResult.Dispose();
|
||||
msResult.Dispose();
|
||||
psResult.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public ref struct ShaderCompilationConfig
|
||||
{
|
||||
public ReadOnlySpan<string> defines;
|
||||
public ReadOnlySpan<string> includes;
|
||||
public string shaderPath;
|
||||
public string shaderCode;
|
||||
public string entryPoint;
|
||||
public string? injectedCode;
|
||||
public ShaderStage stage;
|
||||
public ShaderModel model;
|
||||
public CompilerOptimizeLevel optimizeLevel;
|
||||
@@ -154,7 +146,7 @@ public readonly struct ShaderReflectionData
|
||||
|
||||
public interface IShaderCompiler : IDisposable
|
||||
{
|
||||
Result<ShaderCompileResult> Compile(ref readonly ShaderCompilationConfig config, Allocator allocator);
|
||||
Result<Key64<ShaderCompileResult>> Compile(ref readonly ShaderCompilationConfig config);
|
||||
Result<GraphicsCompiledResult> CompilePass(ref readonly PassDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, ref readonly LocalKeywordSet keywords);
|
||||
Result<GraphicsCompiledResult, Error> LoadCompiledCache(Key64<ShaderVariant> key);
|
||||
Result<ShaderCompileResult, Error> GetCompiledCache(Key64<ShaderCompileResult> key);
|
||||
}
|
||||
|
||||
@@ -156,11 +156,10 @@ public static class RHIUtility
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Key64<ShaderVariant> CreateShaderVariantKey(Key64<ShaderPass> passKey, ref readonly LocalKeywordSet keywords)
|
||||
public static Key64<ShaderVariant> CreateShaderVariantKey(ulong passKey, ref readonly LocalKeywordSet keywords)
|
||||
{
|
||||
var passHash = passKey.Value;
|
||||
var keywordHash = keywords.GetHash64();
|
||||
return new Key64<ShaderVariant>(Hash.Combine64(passHash, keywordHash));
|
||||
return new Key64<ShaderVariant>(Hash.Combine64(passKey, keywordHash));
|
||||
}
|
||||
|
||||
public static unsafe Key128<GraphicsPipeline> CreateGraphicsPipelineKey(Key64<ShaderVariant> shaderVariantKey, PipelineState pipelineState, PassPipelineHash passKey)
|
||||
@@ -192,10 +191,10 @@ public static class RHIUtility
|
||||
return new Key128<GraphicsPipeline>(new UInt128(hi, lo));
|
||||
}
|
||||
|
||||
public static Key128<ComputePipeline> CreateComputePipelineKey(Key64<ShaderVariant> shaderVariantKey, ulong compiledHash)
|
||||
public static Key128<ComputePipeline> CreateComputePipelineKey(Key64<ShaderVariant> shaderVariantKey)
|
||||
{
|
||||
var shaderHash = shaderVariantKey.Value;
|
||||
var stateHash = compiledHash;
|
||||
var stateHash = ~shaderVariantKey.Value;
|
||||
// Simple XOR mix. Not as robust as the graphics pipeline key, but sufficient for compute shaders which have fewer variants.
|
||||
var hi = shaderHash ^ (stateHash + 0x9E3779B97F4A7C15ul) ^ (shaderHash * 0xD6E8FEB86659FD93ul);
|
||||
var lo = stateHash ^ (shaderHash + 0xC2B2AE3D27D4EB4Ful) ^ (stateHash * 0x165667B19E3779F9ul);
|
||||
|
||||
@@ -50,14 +50,14 @@ public struct Material : IResourceReleasable
|
||||
public PipelineState options;
|
||||
}
|
||||
|
||||
private Identifier<Shader> _shader;
|
||||
private Handle<Shader> _shader;
|
||||
private UnsafeArray<PipelineOverride> _passPipelineOverride;
|
||||
private bool _isDirty;
|
||||
|
||||
internal CBufferCache _cBufferCache;
|
||||
internal LocalKeywordSet _keywordMask;
|
||||
|
||||
public readonly Identifier<Shader> Shader => _shader;
|
||||
public readonly Handle<Shader> Shader => _shader;
|
||||
public readonly bool IsDirty => _isDirty;
|
||||
|
||||
public int ActivePassIndex
|
||||
@@ -71,7 +71,7 @@ public struct Material : IResourceReleasable
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Error SetShader(Identifier<Shader> shaderId, ResourceManager resourceManager, IResourceDatabase resourceDatabase, IResourceAllocator resourceAllocator)
|
||||
public Error SetShader(Handle<Shader> shaderId, ResourceManager resourceManager, IResourceDatabase resourceDatabase, IResourceAllocator resourceAllocator)
|
||||
{
|
||||
if (!shaderId.IsValid)
|
||||
{
|
||||
|
||||
@@ -179,7 +179,7 @@ public struct Mesh : IResourceReleasable
|
||||
/// <summary>
|
||||
/// Gets the handle to the mesh data buffer on the GPU.
|
||||
/// </summary>
|
||||
public Handle<GPUBuffer> ObjectDataBuffer
|
||||
public Handle<GPUBuffer> MeshDataBuffer
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
@@ -364,7 +364,7 @@ public struct Mesh : IResourceReleasable
|
||||
database.ReleaseResource(MeshLetBuffer.AsResource());
|
||||
database.ReleaseResource(MeshletVerticesBuffer.AsResource());
|
||||
database.ReleaseResource(MeshletTrianglesBuffer.AsResource());
|
||||
database.ReleaseResource(ObjectDataBuffer.AsResource());
|
||||
database.ReleaseResource(MeshDataBuffer.AsResource());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,22 +10,21 @@ namespace Ghost.Graphics.Core;
|
||||
// TODO: Temporary rendering context for heap creation and data upload. We will refactor it later when we have a better understanding of the engine architecture.
|
||||
public readonly unsafe ref struct RenderContext
|
||||
{
|
||||
private readonly IGraphicsEngine _engine;
|
||||
private readonly ResourceManager _resourceManager;
|
||||
private readonly IGraphicsEngine _engine;
|
||||
private readonly ICommandBuffer _cmd;
|
||||
|
||||
public ICommandBuffer CommandBuffer => _cmd;
|
||||
|
||||
public IShaderCompiler ShaderCompiler => _engine.ShaderCompiler;
|
||||
public ResourceManager ResourceManager => _resourceManager;
|
||||
public IResourceAllocator ResourceAllocator => _engine.ResourceAllocator;
|
||||
public IResourceDatabase ResourceDatabase => _engine.ResourceDatabase;
|
||||
public IPipelineLibrary PipelineLibrary => _engine.PipelineLibrary;
|
||||
|
||||
internal RenderContext(IGraphicsEngine engine, ResourceManager resourceManager, ICommandBuffer cmd)
|
||||
internal RenderContext(ResourceManager resourceManager, IGraphicsEngine engine, ICommandBuffer cmd)
|
||||
{
|
||||
_engine = engine;
|
||||
_resourceManager = resourceManager;
|
||||
_engine = engine;
|
||||
_cmd = cmd;
|
||||
}
|
||||
|
||||
@@ -242,10 +241,10 @@ public readonly unsafe ref struct RenderContext
|
||||
meshletTrianglesBuffer = _engine.ResourceDatabase.GetBindlessIndex(meshData.MeshletTrianglesBuffer.AsResource()),
|
||||
};
|
||||
|
||||
var bufferHandle = meshData.ObjectDataBuffer.AsResource();
|
||||
var bufferHandle = meshData.MeshDataBuffer.AsResource();
|
||||
|
||||
TransitionBarrier(bufferHandle, false, BarrierLayout.Undefined, BarrierAccess.CopyDest, BarrierSync.Copy);
|
||||
UploadBuffer(meshData.ObjectDataBuffer, data);
|
||||
UploadBuffer(meshData.MeshDataBuffer, data);
|
||||
TransitionBarrier(bufferHandle, false, BarrierLayout.Undefined, BarrierAccess.ShaderResource, BarrierSync.PixelShading | BarrierSync.NonPixelShading);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RenderPipeline;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -179,22 +178,11 @@ public struct RenderView
|
||||
public RenderingLayerMask renderingLayerMask;
|
||||
}
|
||||
|
||||
public struct RenderRequest: IDisposable
|
||||
public struct RenderRequest
|
||||
{
|
||||
public RenderView view;
|
||||
|
||||
public int swapChainIndex;
|
||||
public Handle<GPUTexture> colorTarget;
|
||||
public Handle<GPUTexture> depthTarget;
|
||||
|
||||
public RenderList opaqueRenderList;
|
||||
public RenderList transparentRenderList;
|
||||
public RenderList shadowCasterRenderList;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
opaqueRenderList.Dispose();
|
||||
transparentRenderList.Dispose();
|
||||
shadowCasterRenderList.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +87,10 @@ public partial struct Shader : IResourceReleasable
|
||||
public readonly int PassCount => _shaderPasses.Count;
|
||||
public readonly uint PropertyBufferSize => _propertyBufferSize;
|
||||
|
||||
internal Shader(GraphicsShaderDescriptor descriptor, ref readonly GraphicsCompiledResult compiledResult)
|
||||
internal Shader(GraphicsShaderDescriptor descriptor, ReadOnlySpan<GraphicsCompiledResult> compiledResults)
|
||||
{
|
||||
Debug.Assert(descriptor.passes.Length == compiledResults.Length);
|
||||
|
||||
_propertyBufferSize = descriptor.propertyBufferSize;
|
||||
_shaderPasses = new UnsafeArray<ShaderPass>(descriptor.passes.Length, Allocator.Persistent);
|
||||
_passIDToLocal = new UnsafeHashMap<int, int>(descriptor.passes.Length, Allocator.Persistent);
|
||||
@@ -98,7 +100,7 @@ public partial struct Shader : IResourceReleasable
|
||||
{
|
||||
ref readonly var pass = ref descriptor.passes[i];
|
||||
|
||||
var passKey = RHIUtility.CreateShaderPassKey(pass.identifier, compiledResult.HashCode);
|
||||
var passKey = RHIUtility.CreateShaderPassKey(pass.identifier, compiledResults[i].HashCode);
|
||||
var keywords = default(LocalKeywordSet);
|
||||
|
||||
if (pass.keywords.Length > 0)
|
||||
@@ -141,7 +143,7 @@ public partial struct Shader : IResourceReleasable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal readonly int GetLocalKeywordIndex(int globalKeywordID)
|
||||
internal int GetLocalKeywordIndex(int globalKeywordID)
|
||||
{
|
||||
if (_keywordIDToLocal.TryGetValue(globalKeywordID, out var localIndex))
|
||||
{
|
||||
@@ -152,7 +154,7 @@ public partial struct Shader : IResourceReleasable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly int GetPassIndex(Identifier<ShaderPass> passID)
|
||||
public int GetPassIndex(Identifier<ShaderPass> passID)
|
||||
{
|
||||
if (_passIDToLocal.TryGetValue(passID.Value, out var index))
|
||||
{
|
||||
@@ -163,7 +165,7 @@ public partial struct Shader : IResourceReleasable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly int GetPassIndex(string passName)
|
||||
public int GetPassIndex(string passName)
|
||||
{
|
||||
if (_passIDToLocal.TryGetValue(GetPassID(passName), out var index))
|
||||
{
|
||||
@@ -174,13 +176,13 @@ public partial struct Shader : IResourceReleasable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly ref ShaderPass GetPassReference(int index)
|
||||
public ref ShaderPass GetPassReference(int index)
|
||||
{
|
||||
return ref _shaderPasses[index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly Result<ShaderPass, Error> TryGetPass(Identifier<ShaderPass> passID, out int passIndex)
|
||||
public Result<ShaderPass, Error> TryGetPass(Identifier<ShaderPass> passID, out int passIndex)
|
||||
{
|
||||
if (_passIDToLocal.TryGetValue(passID.Value, out var index))
|
||||
{
|
||||
@@ -200,25 +202,52 @@ public partial struct Shader : IResourceReleasable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public unsafe partial struct ComputeShader
|
||||
public unsafe partial struct ComputeShader : IResourceReleasable
|
||||
{
|
||||
private fixed ulong _entryHash[8];
|
||||
private readonly int _entryPointCount;
|
||||
private readonly uint _propertyBufferSize;
|
||||
|
||||
private LocalKeywordSet _localKeywordSet;
|
||||
private UnsafeHashMap<int, int> _keywordIDToLocal;
|
||||
|
||||
public readonly uint PropertyBufferSize => _propertyBufferSize;
|
||||
|
||||
internal ComputeShader(ComputeShaderDescriptor descriptor, ReadOnlySpan<ShaderCompileResult> compiledResults)
|
||||
{
|
||||
Debug.Assert(descriptor.entryPoints.Length == compiledResults.Length);
|
||||
Debug.Assert(descriptor.shaderCodes.Length == compiledResults.Length);
|
||||
|
||||
_propertyBufferSize = descriptor.propertyBufferSize;
|
||||
_entryPointCount = descriptor.entryPoints.Length;
|
||||
for (var i = 0; i < descriptor.entryPoints.Length; i++)
|
||||
_entryPointCount = descriptor.shaderCodes.Length;
|
||||
|
||||
_keywordIDToLocal = new UnsafeHashMap<int, int>(32, Allocator.Persistent);
|
||||
|
||||
for (var i = 0; i < descriptor.shaderCodes.Length; i++)
|
||||
{
|
||||
_entryHash[i] = Hash.Combine64(descriptor.identifier, compiledResults[i].hashCode);
|
||||
}
|
||||
|
||||
var localKeywordIndex = 0;
|
||||
for (var i = 0; i < descriptor.keywords.Length; i++)
|
||||
{
|
||||
var group = descriptor.keywords[i];
|
||||
if (group.keywords == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (group.space == KeywordSpace.Local)
|
||||
{
|
||||
foreach (var kw in group.keywords)
|
||||
{
|
||||
var kwID = Shader.GetKeywordID(kw);
|
||||
var idx = localKeywordIndex++;
|
||||
|
||||
_localKeywordSet.SetKeyword(idx, true);
|
||||
_keywordIDToLocal.TryAdd(kwID, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetEntryHash(int entryPointIndex)
|
||||
@@ -226,4 +255,20 @@ public unsafe partial struct ComputeShader
|
||||
Debug.Assert(entryPointIndex >= 0 && entryPointIndex < _entryPointCount);
|
||||
return _entryHash[entryPointIndex];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal int GetLocalKeywordIndex(int globalKeywordID)
|
||||
{
|
||||
if (_keywordIDToLocal.TryGetValue(globalKeywordID, out var localIndex))
|
||||
{
|
||||
return localIndex;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void ReleaseResource(IResourceDatabase database)
|
||||
{
|
||||
_keywordIDToLocal.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,11 @@ public sealed class RenderGraph : IDisposable
|
||||
_blackboard = new RenderGraphBlackboard();
|
||||
}
|
||||
|
||||
public RenderGraph(ResourceManager resourceManager, IGraphicsEngine graphicsEngine)
|
||||
: this(resourceManager, graphicsEngine.ResourceAllocator, graphicsEngine.ResourceDatabase, graphicsEngine.PipelineLibrary, graphicsEngine.ShaderCompiler)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the render graph for a new frame.
|
||||
/// </summary>
|
||||
|
||||
@@ -36,6 +36,7 @@ public interface IRasterRenderContext : IRenderGraphContext
|
||||
|
||||
public interface IComputeRenderContext : IRenderGraphContext
|
||||
{
|
||||
void SetActiveCompute(Handle<ComputeShader> computeShader, uint entryIndex);
|
||||
void DispatchCompute(uint3 threadGroupCount);
|
||||
}
|
||||
|
||||
@@ -141,8 +142,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
var r = _resourceManager.GetMaterialReference(material);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
_activePerMaterialData = Handle<GPUBuffer>.Invalid;
|
||||
return;
|
||||
throw new InvalidOperationException($"Failed to get material reference for material handle {material}.");
|
||||
}
|
||||
|
||||
ref readonly var mat = ref r.Value;
|
||||
@@ -158,8 +158,8 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
return;
|
||||
}
|
||||
|
||||
ref readonly var shader = ref shaderResult.Value;
|
||||
ref readonly var pass = ref shader.GetPassReference(material.ActivePassIndex);
|
||||
ref var shader = ref shaderResult.Value;
|
||||
ref var pass = ref shader.GetPassReference(material.ActivePassIndex);
|
||||
|
||||
var passPipelineHash = new PassPipelineHash(_rtvFormats, _dsvFormat);
|
||||
var materialPipeline = material.GetPassPipelineOverride(material.ActivePassIndex);
|
||||
@@ -169,9 +169,9 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
var shaderVariantKey = RHIUtility.CreateShaderVariantKey(pass.Key, in variantMask);
|
||||
var pipelineKey = RHIUtility.CreateGraphicsPipelineKey(shaderVariantKey, materialPipeline, passPipelineHash);
|
||||
|
||||
if (!_pipelineLibrary.HasPipeline(pipelineKey))
|
||||
if (!_pipelineLibrary.HasPipelineStateObject(pipelineKey))
|
||||
{
|
||||
var compiledCacheResult = _shaderCompiler.LoadCompiledCache(shaderVariantKey);
|
||||
var compiledCacheResult = _shaderCompiler.GetCompiledCache(shaderVariantKey);
|
||||
if (compiledCacheResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to load compiled shader cache for pipeline state object creation.");
|
||||
@@ -199,9 +199,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
var r = _resourceManager.GetMeshReference(mesh);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
_activePerMeshData = Handle<GPUBuffer>.Invalid;
|
||||
_activeMeshIndexCount = 0;
|
||||
return;
|
||||
throw new InvalidOperationException($"Failed to get mesh reference for mesh handle {mesh}.");
|
||||
}
|
||||
|
||||
ref readonly var meshRef = ref r.Value;
|
||||
@@ -210,7 +208,7 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
|
||||
public void SetActiveMesh(ref readonly Mesh mesh)
|
||||
{
|
||||
_activePerMeshData = mesh.ObjectDataBuffer;
|
||||
_activePerMeshData = mesh.MeshDataBuffer;
|
||||
_activeMeshIndexCount = mesh.IndexCount;
|
||||
}
|
||||
|
||||
@@ -239,12 +237,41 @@ internal sealed class RenderGraphContext : IUnsafeRenderContext
|
||||
_commandBuffer.DispatchMesh(threadGroupCount.x, threadGroupCount.y, threadGroupCount.z);
|
||||
}
|
||||
|
||||
public void SetActiveCompute(Handle<ComputeShader> computeShader, uint entryIndex)
|
||||
{
|
||||
var r = _resourceManager.GetComputeShaderReference(computeShader);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to get compute shader reference for handle {computeShader}.");
|
||||
}
|
||||
|
||||
ref var shader = ref r.Value;
|
||||
var entryHash = shader.GetEntryHash((int)entryIndex);
|
||||
var keywordSet = new LocalKeywordSet(); // TODO: Support keywords in compute shader.
|
||||
var variantKey = RHIUtility.CreateShaderVariantKey(entryHash, in keywordSet);
|
||||
var pipelineKey = RHIUtility.CreateComputePipelineKey(variantKey);
|
||||
|
||||
if (!_pipelineLibrary.HasPipelineStateObject(pipelineKey))
|
||||
{
|
||||
var compiledCacheResult = _shaderCompiler.GetCompiledCache(variantKey);
|
||||
if (compiledCacheResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to load compiled shader cache for pipeline state object creation.");
|
||||
}
|
||||
var psoDes = new ComputePSODescriptor
|
||||
{
|
||||
VariantKey = variantKey,
|
||||
};
|
||||
var compiled = compiledCacheResult.Value;
|
||||
_pipelineLibrary.CreateComputePipeline(in psoDes, in compiled).GetValueOrThrow();
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchCompute(uint3 threadGroupCount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public ICommandBuffer GetCommandBufferUnsafe()
|
||||
{
|
||||
return _commandBuffer;
|
||||
|
||||
@@ -24,10 +24,9 @@ public interface IRenderPipeline : IDisposable
|
||||
void Render(RenderContext ctx, int frameIndex, IRenderPayload payload);
|
||||
}
|
||||
|
||||
|
||||
public static class RenderPipelineUtility
|
||||
{
|
||||
public static bool GetViewAndProjectionMatrices(RenderSystem renderSystem, ref readonly RenderRequest request, out float4x4 view, out float4x4 projection, out uint2 screenSize)
|
||||
public static bool GetVPMatrices(RenderSystem renderSystem, ref readonly RenderRequest request, out float4x4 view, out float4x4 projection, out uint2 screenSize)
|
||||
{
|
||||
Handle<GPUTexture> rtHandle;
|
||||
if (request.swapChainIndex < 0)
|
||||
@@ -2,6 +2,7 @@ using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Services;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
@@ -280,7 +281,7 @@ public class RenderSystem : IDisposable
|
||||
{
|
||||
cmd.Begin(frameResource.CommandAllocator);
|
||||
|
||||
var ctx = new RenderContext(_graphicsEngine, _resourceManager, cmd);
|
||||
var ctx = new RenderContext(_resourceManager, _graphicsEngine, cmd);
|
||||
|
||||
_renderPipeline.Render(ctx, frameIndex, frameResource.RenderPayload);
|
||||
_swapChainManager.TransitionToPresent(cmd);
|
||||
|
||||
@@ -5,7 +5,6 @@ using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
@@ -29,11 +28,13 @@ public sealed partial class ResourceManager : IDisposable
|
||||
|
||||
private UnsafeSlotMap<Mesh> _meshes;
|
||||
private UnsafeSlotMap<Material> _materials;
|
||||
private UnsafeList<Shader> _shaders; // TODO: Use SlotMap?
|
||||
private UnsafeSlotMap<Shader> _shaders;
|
||||
private UnsafeSlotMap<ComputeShader> _computeShaders;
|
||||
|
||||
private readonly MaterialPaletteStore _materialPalettes;
|
||||
|
||||
private ulong _submittedFrame;
|
||||
private int _writeLock;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
@@ -45,7 +46,8 @@ public sealed partial class ResourceManager : IDisposable
|
||||
|
||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
||||
_materials = new UnsafeSlotMap<Material>(64, Allocator.Persistent);
|
||||
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
||||
_shaders = new UnsafeSlotMap<Shader>(16, Allocator.Persistent);
|
||||
_computeShaders = new UnsafeSlotMap<ComputeShader>(16, Allocator.Persistent);
|
||||
|
||||
_materialPalettes = new MaterialPaletteStore();
|
||||
}
|
||||
@@ -67,6 +69,18 @@ public sealed partial class ResourceManager : IDisposable
|
||||
EndFramePool(completedFrame);
|
||||
}
|
||||
|
||||
public void EnterParallelRead()
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
Volatile.Write(ref _writeLock, 1);
|
||||
}
|
||||
|
||||
public void ExitParallelRead()
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
Volatile.Write(ref _writeLock, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh from the specified vertex and index data.
|
||||
/// </summary>
|
||||
@@ -77,80 +91,139 @@ public sealed partial class ResourceManager : IDisposable
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
var spinner = new SpinWait();
|
||||
while (Interlocked.CompareExchange(ref _writeLock, 1, 0) != 0)
|
||||
{
|
||||
Size = (uint)(vertices.Count * sizeof(Vertex)),
|
||||
Stride = (uint)sizeof(Vertex),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
spinner.SpinOnce();
|
||||
}
|
||||
|
||||
var indexBufferDesc = new BufferDesc
|
||||
try
|
||||
{
|
||||
Size = (uint)(indices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(vertices.Count * sizeof(Vertex)),
|
||||
Stride = (uint)sizeof(Vertex),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var objectBufferDesc = new BufferDesc
|
||||
var indexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(indices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var objectBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)sizeof(MeshData),
|
||||
Stride = (uint)sizeof(MeshData),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer");
|
||||
var indexBuffer = _resourceAllocator.CreateBuffer(in indexBufferDesc, "IndexBuffer");
|
||||
var objectBuffer = _resourceAllocator.CreateBuffer(in objectBufferDesc, "ObjectBuffer");
|
||||
|
||||
var mesh = new Mesh
|
||||
{
|
||||
Vertices = vertices,
|
||||
Indices = indices,
|
||||
VertexBuffer = vertexBuffer,
|
||||
IndexBuffer = indexBuffer,
|
||||
MeshDataBuffer = objectBuffer,
|
||||
};
|
||||
|
||||
var id = _meshes.Add(mesh, out var generation);
|
||||
return new Handle<Mesh>(id, generation);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Size = (uint)sizeof(MeshData),
|
||||
Stride = (uint)sizeof(MeshData),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
HeapType = HeapType.Default,
|
||||
};
|
||||
|
||||
var vertexBuffer = _resourceAllocator.CreateBuffer(in vertexBufferDesc, "VertexBuffer");
|
||||
var indexBuffer = _resourceAllocator.CreateBuffer(in indexBufferDesc, "IndexBuffer");
|
||||
var objectBuffer = _resourceAllocator.CreateBuffer(in objectBufferDesc, "ObjectBuffer");
|
||||
|
||||
var mesh = new Mesh
|
||||
{
|
||||
Vertices = vertices,
|
||||
Indices = indices,
|
||||
VertexBuffer = vertexBuffer,
|
||||
IndexBuffer = indexBuffer,
|
||||
ObjectDataBuffer = objectBuffer,
|
||||
};
|
||||
|
||||
var id = _meshes.Add(mesh, out var generation);
|
||||
return new Handle<Mesh>(id, generation);
|
||||
Volatile.Write(ref _writeLock, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new material instance using the specified shader.
|
||||
/// </summary>
|
||||
/// <param name="shader">The identifier of the shader to associate with the new material.</param>
|
||||
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
|
||||
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
||||
/// <returns>An <see cref="Handle{Material}"/> representing the newly created material.</returns>
|
||||
public Handle<Material> CreateMaterial(Handle<Shader> shader)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var material = new Material();
|
||||
if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None)
|
||||
var spinner = new SpinWait();
|
||||
while (Interlocked.CompareExchange(ref _writeLock, 1, 0) != 0)
|
||||
{
|
||||
return Handle<Material>.Invalid;
|
||||
spinner.SpinOnce();
|
||||
}
|
||||
|
||||
var id = _materials.Add(material, out var generation);
|
||||
return new Handle<Material>(id, generation);
|
||||
try
|
||||
{
|
||||
var material = new Material();
|
||||
if (material.SetShader(shader, this, _resourceDatabase, _resourceAllocator) != Error.None)
|
||||
{
|
||||
return Handle<Material>.Invalid;
|
||||
}
|
||||
|
||||
var id = _materials.Add(material, out var generation);
|
||||
return new Handle<Material>(id, generation);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Volatile.Write(ref _writeLock, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader and returns its unique identifier.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <returns>An <see cref="Handle{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
public Identifier<Shader> CreateGraphicsShader(GraphicsShaderDescriptor descriptor, ref readonly GraphicsCompiledResult compiledResult)
|
||||
public Handle<Shader> CreateGraphicsShader(GraphicsShaderDescriptor descriptor, ReadOnlySpan<GraphicsCompiledResult> compiledResults)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
var shader = new Shader(descriptor, in compiledResult);
|
||||
var spinner = new SpinWait();
|
||||
while (Interlocked.CompareExchange(ref _writeLock, 1, 0) != 0)
|
||||
{
|
||||
spinner.SpinOnce();
|
||||
}
|
||||
|
||||
var id = _shaders.Count;
|
||||
_shaders.Add(shader);
|
||||
return new Identifier<Shader>(id);
|
||||
try
|
||||
{
|
||||
var shader = new Shader(descriptor, compiledResults);
|
||||
|
||||
var id = _shaders.Add(shader, out var generation);
|
||||
return new Handle<Shader>(id, generation);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Volatile.Write(ref _writeLock, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public Handle<ComputeShader> CreateComputeShader(ComputeShaderDescriptor descriptor, ReadOnlySpan<ShaderCompileResult> compiledResults)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
var spinner = new SpinWait();
|
||||
while (Interlocked.CompareExchange(ref _writeLock, 1, 0) != 0)
|
||||
{
|
||||
spinner.SpinOnce();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var computeShader = new ComputeShader(descriptor, compiledResults);
|
||||
var id = _computeShaders.Add(computeShader, out var generation);
|
||||
return new Handle<ComputeShader>(id, generation);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Volatile.Write(ref _writeLock, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -309,44 +382,91 @@ public sealed partial class ResourceManager : IDisposable
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to check for existence.</param>
|
||||
/// <returns>true if a shader with the specified identifier exists; otherwise, false.</returns>
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
public bool HasShader(Handle<Shader> id)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
return id.Value >= 0 && id.Value < _shaders.Count;
|
||||
return _shaders.Contains(id.ID, id.Generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the shader associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
|
||||
/// <param name="handle">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
|
||||
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
|
||||
public RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id)
|
||||
public RefResult<Shader, Error> GetShaderReference(Handle<Shader> handle)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
ref var shader = ref _shaders.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Shader, Error>.Success(ref _shaders[id.Value]);
|
||||
return RefResult<Shader, Error>.Success(ref shader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
|
||||
public void ReleaseShader(Identifier<Shader> id)
|
||||
/// <param name="handle">The identifier of the shader to release. Must refer to a valid, previously created shader.</param>
|
||||
public void ReleaseShader(Handle<Shader> handle)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
if (!HasShader(id))
|
||||
ref var shader = ref _shaders.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var shader = ref _shaders[id.Value];
|
||||
_shaders.Remove(handle.ID, handle.Generation);
|
||||
shader.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a compute shader with the specified identifier exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the compute shader to check for existence.</param>
|
||||
/// <returns>true if a compute shader with the specified identifier exists; otherwise, false.</returns>
|
||||
public bool HasComputeShader(Handle<ComputeShader> id)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
return _computeShaders.Contains(id.ID, id.Generation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the compute shader associated with the specified identifier.
|
||||
/// </summary>
|
||||
/// <param name="handle">The identifier of the compute shader to retrieve. Must refer to a valid ComputeShader.</param>
|
||||
/// <returns>A result containing a reference to the compute shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
|
||||
public RefResult<ComputeShader, Error> GetComputeShaderReference(Handle<ComputeShader> handle)
|
||||
{
|
||||
ref var computeShader = ref _computeShaders.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<ComputeShader, Error>.Success(ref computeShader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the compute shader associated with the specified identifier, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="handle">The identifier of the compute shader to release. Must refer to a valid, previously created ComputeShader.</param>
|
||||
public void ReleaseComputeShader(Handle<ComputeShader> handle)
|
||||
{
|
||||
Debug.Assert(!_disposed);
|
||||
|
||||
ref var computeShader = ref _computeShaders.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_computeShaders.Remove(handle.ID, handle.Generation);
|
||||
computeShader.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -1,7 +1,7 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
namespace Ghost.Graphics.Services;
|
||||
|
||||
public class ResourceUploadBatch
|
||||
{
|
||||
11
src/Runtime/Ghost.Graphics/Services/ShaderLibrary.cs
Normal file
11
src/Runtime/Ghost.Graphics/Services/ShaderLibrary.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Ghost.Graphics.Services;
|
||||
|
||||
public class ShaderLibrary : IDisposable
|
||||
{
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
namespace Ghost.Graphics.Services;
|
||||
|
||||
internal sealed class SwapChainRecord
|
||||
{
|
||||
@@ -3,6 +3,12 @@
|
||||
|
||||
#include "F:/csharp/GhostEngine/src/Runtime/Ghost.Graphics/Shaders/Includes/Common.hlsl"
|
||||
|
||||
#define GLOBAL_BINDLESS_SIG \
|
||||
"RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | " \
|
||||
"CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | " \
|
||||
"SAMPLER_HEAP_DIRECTLY_INDEXED), " \
|
||||
"RootConstants(num32BitConstants=3, b0, space=0)"
|
||||
|
||||
// TODO: This should be auto generated to match the c# side.
|
||||
|
||||
struct GraphicsPushConstantData
|
||||
@@ -59,6 +65,9 @@ struct MeshData
|
||||
GraphicsPushConstantData g_PushConstantData : register(b0);
|
||||
#elif defined(__COMPUTE__)
|
||||
ComputePushConstantData g_PushConstantData : register(b0);
|
||||
#elif defined(__WORK_GRAPH__)
|
||||
#define WorkGraphPushConstantData ComputePushConstantData
|
||||
WorkGraphPushConstantData g_PushConstantData : register(b0);
|
||||
#endif
|
||||
|
||||
#endif // GHOST_PROPERTIES_HLSL
|
||||
|
||||
Reference in New Issue
Block a user