Refactor pipeline state and render output abstractions
- Replace old pipeline enums/structs with new strongly-typed PipelineState and enums (ZTest, ZWrite, Cull, Blend, ColorWriteMask) - Redesign pipeline keying: introduce 128-bit GraphicsPipelineKey, MaterialPipelineKey, and PassPipelineKey for robust PSO caching - Replace IRenderTargetStrategy with IRenderOutput; add SwapChainRenderOutput and TextureRenderOutput - Update renderer and window code to use new render output abstraction and handle viewport/scissor updates - Make ShaderPass a readonly struct and Shader a struct; use ID-based pass lookup for efficiency - Materials now support per-pass pipeline overrides with new keying - Add defensive checks in D3D12CommandBuffer; update D3D12PipelineLibrary for new keying/state - Move test shader to test.gsdef and update for new pipeline state syntax - Remove obsolete files/interfaces and perform general code cleanups - Update all usages and parsing logic for new pipeline state system
This commit is contained in:
84
Ghost.Core/Graphics/PipelineState.cs
Normal file
84
Ghost.Core/Graphics/PipelineState.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
namespace Ghost.Core.Graphics;
|
||||||
|
|
||||||
|
public enum ZTest
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
Less,
|
||||||
|
LessEqual,
|
||||||
|
Equal,
|
||||||
|
GreaterEqual,
|
||||||
|
Greater,
|
||||||
|
NotEqual,
|
||||||
|
Always
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ZWrite
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
On
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Cull
|
||||||
|
{
|
||||||
|
Off,
|
||||||
|
Front,
|
||||||
|
Back
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Blend
|
||||||
|
{
|
||||||
|
Opaque,
|
||||||
|
Alpha,
|
||||||
|
Additive,
|
||||||
|
Multiply,
|
||||||
|
PremultipliedAlpha
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ColorWriteMask
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Red = 1 << 0,
|
||||||
|
Green = 1 << 1,
|
||||||
|
Blue = 1 << 2,
|
||||||
|
Alpha = 1 << 3,
|
||||||
|
All = Red | Green | Blue | Alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct PipelineState
|
||||||
|
{
|
||||||
|
public ZTest ZTest
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZWrite ZWrite
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cull Cull
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Blend Blend
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorWriteMask ColorMask
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static PipelineState Default => new PipelineState
|
||||||
|
{
|
||||||
|
ZTest = ZTest.LessEqual,
|
||||||
|
ZWrite = ZWrite.On,
|
||||||
|
Cull = Cull.Back,
|
||||||
|
Blend = Blend.Opaque,
|
||||||
|
ColorMask = ColorWriteMask.All
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
namespace Ghost.Core.Graphics;
|
|
||||||
|
|
||||||
public enum ZTestOptions
|
|
||||||
{
|
|
||||||
Disabled,
|
|
||||||
Less,
|
|
||||||
LessEqual,
|
|
||||||
Equal,
|
|
||||||
GreaterEqual,
|
|
||||||
Greater,
|
|
||||||
NotEqual,
|
|
||||||
Always
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ZWriteOptions
|
|
||||||
{
|
|
||||||
Off,
|
|
||||||
On
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CullOptions
|
|
||||||
{
|
|
||||||
Off,
|
|
||||||
Front,
|
|
||||||
Back
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum BlendOptions
|
|
||||||
{
|
|
||||||
Opaque,
|
|
||||||
Alpha,
|
|
||||||
Additive,
|
|
||||||
Multiply,
|
|
||||||
PremultipliedAlpha
|
|
||||||
}
|
|
||||||
@@ -33,24 +33,6 @@ public struct KeywordsGroup
|
|||||||
public List<string>? keywords;
|
public List<string>? keywords;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PipelineDescriptor
|
|
||||||
{
|
|
||||||
public ZTestOptions zTest;
|
|
||||||
public ZWriteOptions zWrite;
|
|
||||||
public CullOptions cull;
|
|
||||||
public BlendOptions blend;
|
|
||||||
public uint colorMask;
|
|
||||||
|
|
||||||
public static PipelineDescriptor Default = new PipelineDescriptor
|
|
||||||
{
|
|
||||||
zTest = ZTestOptions.LessEqual,
|
|
||||||
zWrite = ZWriteOptions.On,
|
|
||||||
cull = CullOptions.Back,
|
|
||||||
blend = BlendOptions.Opaque,
|
|
||||||
colorMask = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IPassDescriptor
|
public interface IPassDescriptor
|
||||||
{
|
{
|
||||||
public string Identifier
|
public string Identifier
|
||||||
@@ -81,7 +63,7 @@ public class FullPassDescriptor : IPassDescriptor
|
|||||||
public ShaderEntryPoint pixelShader;
|
public ShaderEntryPoint pixelShader;
|
||||||
public List<string>? defines;
|
public List<string>? defines;
|
||||||
public List<KeywordsGroup>? keywords;
|
public List<KeywordsGroup>? keywords;
|
||||||
public PipelineDescriptor localPipeline;
|
public PipelineState localPipeline;
|
||||||
|
|
||||||
public string Identifier => uniqueIdentifier;
|
public string Identifier => uniqueIdentifier;
|
||||||
public string Name => name;
|
public string Name => name;
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ namespace Ghost.Editor.Core.SceneGraph;
|
|||||||
|
|
||||||
public class SceneNode : SceneGraphNode
|
public class SceneNode : SceneGraphNode
|
||||||
{
|
{
|
||||||
|
public override SceneGraphNodeType NodeType => SceneGraphNodeType.Scene;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
Target = SwapChainTarget.FromCompositionSurface(Panel)
|
Target = SwapChainTarget.FromCompositionSurface(Panel)
|
||||||
});
|
});
|
||||||
|
|
||||||
_renderer.RenderTargetStrategy = new SwapChainTargetStrategy(_swapChain);
|
_renderer.RenderOutput = new SwapChainRenderOutput(_swapChain);
|
||||||
|
|
||||||
_renderSystem.Start();
|
_renderSystem.Start();
|
||||||
CompositionTarget.Rendering += OnRendering;
|
CompositionTarget.Rendering += OnRendering;
|
||||||
@@ -78,7 +78,7 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
|
|
||||||
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
private void SwapChainPanel_SizeChanged(object sender, SizeChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_renderSystem == null || _swapChain == null)
|
if (_renderSystem == null || _swapChain == null || _renderer == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -92,6 +92,8 @@ public sealed partial class GraphicsTestWindow : Window
|
|||||||
}
|
}
|
||||||
|
|
||||||
_renderSystem.RequestSwapChainResize(_swapChain, new uint2(newWidth, newHeight));
|
_renderSystem.RequestSwapChainResize(_swapChain, new uint2(newWidth, newHeight));
|
||||||
|
_renderer.RenderOutput!.Viewport = new ViewportDesc { Width = newWidth, Height = newHeight, MinDepth = 0.0f, MaxDepth = 1.0f };
|
||||||
|
_renderer.RenderOutput!.Scissor = new RectDesc { Right = newWidth, Bottom = newHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwapChainPanel_CompositionScaleChanged(SwapChainPanel sender, object args)
|
private void SwapChainPanel_CompositionScaleChanged(SwapChainPanel sender, object args)
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ using Ghost.Graphics.RHI;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Contracts;
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
public interface IRenderTargetStrategy
|
public interface IRenderOutput
|
||||||
{
|
{
|
||||||
ViewportDesc Viewport
|
ViewportDesc Viewport
|
||||||
{
|
{
|
||||||
get;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
RectDesc Scissor
|
RectDesc Scissor
|
||||||
{
|
{
|
||||||
get;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -21,16 +21,19 @@ public interface IRenderTargetStrategy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A handle to the texture that is currently set as the render target.</returns>
|
/// <returns>A handle to the texture that is currently set as the render target.</returns>
|
||||||
Handle<Texture> GetRenderTarget();
|
Handle<Texture> GetRenderTarget();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Begins a rendering operation using the specified command buffer. Typically this will include resource barriers,
|
/// Begins a rendering operation using the specified command buffer. Typically this will include resource barriers,
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cmd">The command buffer that records rendering commands.</param>
|
/// <param name="cmd">The command buffer that records rendering commands.</param>
|
||||||
|
///
|
||||||
void BeginRender(ICommandBuffer cmd);
|
void BeginRender(ICommandBuffer cmd);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finalizes the rendering process using the specified command buffer.
|
/// Finalizes the rendering process using the specified command buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cmd">The command buffer that contains the rendering commands to be finalized.</param>
|
/// <param name="cmd">The command buffer that contains the rendering commands to be finalized.</param>
|
||||||
void EndRender(ICommandBuffer cmd);
|
void EndRender(ICommandBuffer cmd);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Displays the current frame to the output device or screen.
|
/// Displays the current frame to the output device or screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Misaki.HighPerformance.Mathematics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -87,6 +88,11 @@ public struct Color128 : IEquatable<Color128>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color128(float4 v)
|
||||||
|
: this(v.x, v.y, v.z, v.w)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public readonly bool Equals(Color128 other)
|
public readonly bool Equals(Color128 other)
|
||||||
{
|
{
|
||||||
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
|
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
|
using Ghost.Core.Graphics;
|
||||||
using Ghost.Graphics.RHI;
|
using Ghost.Graphics.RHI;
|
||||||
using Misaki.HighPerformance.LowLevel;
|
|
||||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
|
#if false
|
||||||
|
public struct VariantMask
|
||||||
|
{
|
||||||
|
private ulong _mask;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
internal struct CBufferCache : IResourceReleasable
|
internal struct CBufferCache : IResourceReleasable
|
||||||
{
|
{
|
||||||
private UnsafeArray<byte> _cpuData;
|
private UnsafeArray<byte> _cpuData;
|
||||||
@@ -49,8 +56,16 @@ internal struct CBufferCache : IResourceReleasable
|
|||||||
|
|
||||||
public struct Material : IResourceReleasable, IHandleType
|
public struct Material : IResourceReleasable, IHandleType
|
||||||
{
|
{
|
||||||
|
private struct PipelineOverride
|
||||||
|
{
|
||||||
|
public ShaderPassKey shaderPass;
|
||||||
|
public PipelineState options;
|
||||||
|
public MaterialPipelineKey pipelineKey;
|
||||||
|
}
|
||||||
|
|
||||||
private Identifier<Shader> _shader;
|
private Identifier<Shader> _shader;
|
||||||
private CBufferCache _cBufferCache;
|
private CBufferCache _cBufferCache;
|
||||||
|
private UnsafeArray<PipelineOverride> _passPipelineOverride;
|
||||||
|
|
||||||
internal readonly CBufferCache CBufferCache => _cBufferCache;
|
internal readonly CBufferCache CBufferCache => _cBufferCache;
|
||||||
|
|
||||||
@@ -67,6 +82,30 @@ public struct Material : IResourceReleasable, IHandleType
|
|||||||
_shader = shaderId;
|
_shader = shaderId;
|
||||||
|
|
||||||
var shader = database.GetShaderReference(shaderId);
|
var shader = database.GetShaderReference(shaderId);
|
||||||
|
|
||||||
|
if (_passPipelineOverride.Count < shader.PassCount)
|
||||||
|
{
|
||||||
|
if (!_passPipelineOverride.IsCreated)
|
||||||
|
{
|
||||||
|
_passPipelineOverride = new UnsafeArray<PipelineOverride>(shader.PassCount, Allocator.Persistent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_passPipelineOverride.Resize(shader.PassCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < shader.PassCount; i++)
|
||||||
|
{
|
||||||
|
var pass = shader.GetPass(i);
|
||||||
|
_passPipelineOverride[i] = new PipelineOverride
|
||||||
|
{
|
||||||
|
shaderPass = pass.Identifier,
|
||||||
|
options = pass.DeafaultState,
|
||||||
|
pipelineKey = new MaterialPipelineKey(pass.Identifier, pass.DeafaultState),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (shader.CBufferSize != 0)
|
if (shader.CBufferSize != 0)
|
||||||
{
|
{
|
||||||
var desc = new BufferDesc
|
var desc = new BufferDesc
|
||||||
@@ -100,7 +139,7 @@ public struct Material : IResourceReleasable, IHandleType
|
|||||||
{
|
{
|
||||||
if (_cBufferCache.Size == 0)
|
if (_cBufferCache.Size == 0)
|
||||||
{
|
{
|
||||||
return Span<byte>.Empty;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return _cBufferCache.CpuData.AsSpan(0, (int)_cBufferCache.Size);
|
return _cBufferCache.CpuData.AsSpan(0, (int)_cBufferCache.Size);
|
||||||
@@ -138,6 +177,26 @@ public struct Material : IResourceReleasable, IHandleType
|
|||||||
cmb.ResourceBarrier(_cBufferCache.GpuResource.AsResource(), ResourceState.VertexAndConstantBuffer);
|
cmb.ResourceBarrier(_cBufferCache.GpuResource.AsResource(), ResourceState.VertexAndConstantBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly PipelineState GetPassPipelineOverride(int passIndex)
|
||||||
|
{
|
||||||
|
return _passPipelineOverride[passIndex].options;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public readonly void SetPassPipelineOverride(int passIndex, in PipelineState options)
|
||||||
|
{
|
||||||
|
ref var pipelineOverride = ref _passPipelineOverride[passIndex];
|
||||||
|
pipelineOverride.options = options;
|
||||||
|
pipelineOverride.pipelineKey = new MaterialPipelineKey(pipelineOverride.shaderPass, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal readonly MaterialPipelineKey GetPassPipelineKey(int passIndex)
|
||||||
|
{
|
||||||
|
return _passPipelineOverride[passIndex].pipelineKey;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Ghost.Graphics.RHI;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
internal class SwapChainTargetStrategy : IRenderTargetStrategy
|
internal class SwapChainRenderOutput : IRenderOutput
|
||||||
{
|
{
|
||||||
private readonly ISwapChain _swapChain;
|
private readonly ISwapChain _swapChain;
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ internal class SwapChainTargetStrategy : IRenderTargetStrategy
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwapChainTargetStrategy(ISwapChain swapChain)
|
public SwapChainRenderOutput(ISwapChain swapChain)
|
||||||
{
|
{
|
||||||
_swapChain = swapChain;
|
_swapChain = swapChain;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ internal class SwapChainTargetStrategy : IRenderTargetStrategy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TextureTargetStrategy : IRenderTargetStrategy
|
internal class TextureRenderOutput : IRenderOutput
|
||||||
{
|
{
|
||||||
private readonly Handle<Texture> _texture;
|
private readonly Handle<Texture> _texture;
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ internal class TextureTargetStrategy : IRenderTargetStrategy
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureTargetStrategy(Handle<Texture> texture)
|
public TextureRenderOutput(Handle<Texture> texture)
|
||||||
{
|
{
|
||||||
_texture = texture;
|
_texture = texture;
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ using Misaki.HighPerformance.LowLevel.Buffer;
|
|||||||
using Misaki.HighPerformance.LowLevel.Collections;
|
using Misaki.HighPerformance.LowLevel.Collections;
|
||||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||||
using Misaki.HighPerformance.Mathematics;
|
using Misaki.HighPerformance.Mathematics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
@@ -163,29 +164,30 @@ public readonly unsafe ref struct RenderingContext
|
|||||||
|
|
||||||
// TODO: Ideally we should queue the draw call to our rendering system, and render it in a 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.
|
// This is just a place holder for now for testing purpose.
|
||||||
public void DispatchMesh(Handle<Mesh> mesh, Handle<Material> material, string passName, uint numThreadsX)
|
public void DispatchMesh(Handle<Mesh> mesh, Handle<Material> material, Identifier<ShaderPass> passID, uint numThreadsX)
|
||||||
{
|
{
|
||||||
ref var meshRef = ref ResourceDatabase.GetMeshReference(mesh);
|
ref var meshRef = ref ResourceDatabase.GetMeshReference(mesh);
|
||||||
ref var materialRef = ref ResourceDatabase.GetMaterialReference(material);
|
ref var materialRef = ref ResourceDatabase.GetMaterialReference(material);
|
||||||
var shader = ResourceDatabase.GetShaderReference(materialRef.Shader);
|
ref var shader = ref ResourceDatabase.GetShaderReference(materialRef.Shader);
|
||||||
|
|
||||||
var keyResult = shader.TryGetPassKey(passName, out var passIndex);
|
var passIndex = shader.GetPassIndex(passID);
|
||||||
if (keyResult.Error != ErrorStatus.None)
|
if (passIndex == -1)
|
||||||
{
|
{
|
||||||
throw new Exception(keyResult.ToString());
|
throw new InvalidOperationException("Shader pass not found in the material's shader.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = new GraphicsPipelineHash
|
var passPipelineKey = new PassPipelineKey([TextureFormat.B8G8R8A8_UNorm], TextureFormat.Unknown);
|
||||||
|
var materialPipelineKey = materialRef.GetPassPipelineKey(passIndex);
|
||||||
|
var pipelineKey = GraphicsPipelineKey.Combine(materialPipelineKey, passPipelineKey);
|
||||||
|
|
||||||
|
if (!_engine.PipelineLibrary.HasPipeline(pipelineKey))
|
||||||
{
|
{
|
||||||
Id = keyResult.Value.Identifier,
|
// TODO: Compile pso if not exist.
|
||||||
RtvCount = 1,
|
// _engine.PipelineLibrary.CompilePSO(pipelineKey, ref shader, passIndex, materialRef.GetPassPipelineOverride());
|
||||||
DsvFormat = TextureFormat.Unknown,
|
throw new InvalidOperationException("Pipeline state object not found in the pipeline library.");
|
||||||
};
|
}
|
||||||
|
|
||||||
hash.RtvFormats[0] = TextureFormat.B8G8R8A8_UNorm;
|
|
||||||
var pipelineKey = hash.GetKey();
|
|
||||||
_directCmd.SetPipelineState(pipelineKey);
|
_directCmd.SetPipelineState(pipelineKey);
|
||||||
|
|
||||||
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer);
|
_directCmd.SetConstantBufferView(RootSignatureLayout.PER_OBJECT_BUFFER_SLOT, meshRef.ObjectDataBuffer);
|
||||||
|
|
||||||
// NOTE: We use fixed root signature layout for bindless rendering.
|
// NOTE: We use fixed root signature layout for bindless rendering.
|
||||||
|
|||||||
@@ -6,63 +6,61 @@ using Misaki.HighPerformance.LowLevel.Collections;
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Core;
|
namespace Ghost.Graphics.Core;
|
||||||
|
|
||||||
public struct ShaderPass : IResourceReleasable
|
public readonly struct ShaderPass : IResourceReleasable
|
||||||
{
|
{
|
||||||
public ShaderPassKey Identifier
|
public ShaderPassKey Identifier
|
||||||
{
|
{
|
||||||
get; init;
|
get; init;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZTestOptions ZTest
|
public PipelineState DeafaultState
|
||||||
{
|
{
|
||||||
get; set;
|
get; init;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZWriteOptions ZWrite
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CullOptions Cull
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlendOptions Blend
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint ColorMask
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Shader variant.
|
|
||||||
|
|
||||||
readonly void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
readonly void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ShaderProperty;
|
||||||
|
|
||||||
|
public partial struct Shader
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, int> s_passNameToID = new Dictionary<string, int>();
|
||||||
|
private static int s_nextPassID = 0;
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, int> s_propertyNameToID = new Dictionary<string, int>();
|
||||||
|
private static int s_nextPropertyID = 0;
|
||||||
|
|
||||||
|
public static Identifier<ShaderPass> GetPassID(string passName)
|
||||||
|
{
|
||||||
|
return new Identifier<ShaderPass>(s_passNameToID.GetValueOrDefault(passName, s_nextPassID++));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Identifier<ShaderProperty> GetPropertyID(string propertyName)
|
||||||
|
{
|
||||||
|
return new Identifier<ShaderProperty>(s_propertyNameToID.GetValueOrDefault(propertyName, s_nextPropertyID++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A representation of a GPU shader, including all the passes it contains.
|
/// A representation of a GPU shader, including all the passes it contains.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Shader : IResourceReleasable, IIdentifierType
|
public partial struct Shader : IResourceReleasable, IIdentifierType
|
||||||
{
|
{
|
||||||
private readonly uint _cbufferSize;
|
private readonly uint _cbufferSize;
|
||||||
private UnsafeArray<ShaderPass> _passes;
|
private UnsafeArray<ShaderPass> _shaderPasses;
|
||||||
// TODO: Optmize lookups with a better data structure if needed
|
private UnsafeHashMap<int, int> _passLookup; // pass id to index
|
||||||
private readonly Dictionary<string, int> _passLookup; // pass name to index
|
|
||||||
|
|
||||||
public int PassCount => _passes.Count;
|
public readonly int PassCount => _shaderPasses.Count;
|
||||||
public uint CBufferSize => _cbufferSize;
|
public readonly uint CBufferSize => _cbufferSize;
|
||||||
|
|
||||||
internal Shader(ShaderDescriptor descriptor)
|
internal Shader(ShaderDescriptor descriptor)
|
||||||
{
|
{
|
||||||
_cbufferSize = descriptor.cbufferSize;
|
_cbufferSize = descriptor.cbufferSize;
|
||||||
_passes = new UnsafeArray<ShaderPass>(descriptor.passes.Count, Allocator.Persistent);
|
_shaderPasses = new UnsafeArray<ShaderPass>(descriptor.passes.Count, Allocator.Persistent);
|
||||||
_passLookup = new Dictionary<string, int>(descriptor.passes.Count);
|
_passLookup = new UnsafeHashMap<int, int>(descriptor.passes.Count, Allocator.Persistent);
|
||||||
|
|
||||||
for (var i = 0; i < descriptor.passes.Count; i++)
|
for (var i = 0; i < descriptor.passes.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -76,45 +74,56 @@ public class Shader : IResourceReleasable, IIdentifierType
|
|||||||
|
|
||||||
var passKey = new ShaderPassKey(pass.Identifier);
|
var passKey = new ShaderPassKey(pass.Identifier);
|
||||||
|
|
||||||
_passes[i] = new ShaderPass
|
_shaderPasses[i] = new ShaderPass
|
||||||
{
|
{
|
||||||
Identifier = passKey,
|
Identifier = passKey,
|
||||||
ZTest = fullPass.localPipeline.zTest,
|
DeafaultState = fullPass.localPipeline
|
||||||
ZWrite = fullPass.localPipeline.zWrite,
|
|
||||||
Cull = fullPass.localPipeline.cull,
|
|
||||||
Blend = fullPass.localPipeline.blend,
|
|
||||||
ColorMask = fullPass.localPipeline.colorMask
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_passLookup[pass.Name] = i;
|
_passLookup[GetPassID(pass.Name)] = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetPassIndex(string passName)
|
public readonly int GetPassIndex(Identifier<ShaderPass> passID)
|
||||||
{
|
{
|
||||||
return _passLookup.GetValueOrDefault(passName, -1);
|
if (_passLookup.TryGetValue(passID.Value, out var index))
|
||||||
|
{
|
||||||
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref ShaderPass GetPassReference(int index)
|
return -1;
|
||||||
{
|
|
||||||
return ref _passes[index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RefResult<ShaderPass, ErrorStatus> TryGetPassKey(string passName, out int passIndex)
|
public readonly int GetPassIndex(string passName)
|
||||||
{
|
{
|
||||||
var index = _passLookup.GetValueOrDefault(passName, -1);
|
if (_passLookup.TryGetValue(GetPassID(passName), out var index))
|
||||||
if (index == -1)
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly ShaderPass GetPass(int index)
|
||||||
|
{
|
||||||
|
return _shaderPasses[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Result<ShaderPass, ErrorStatus> TryGetPass(Identifier<ShaderPass> passID, out int passIndex)
|
||||||
|
{
|
||||||
|
if (_passLookup.TryGetValue(passID.Value, out var index))
|
||||||
{
|
{
|
||||||
passIndex = -1;
|
passIndex = -1;
|
||||||
return ErrorStatus.NotFound;
|
return ErrorStatus.NotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
passIndex = index;
|
passIndex = index;
|
||||||
return RefResult<ShaderPass, ErrorStatus>.Success(ref _passes[index]);
|
return _shaderPasses[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
|
||||||
{
|
{
|
||||||
_passes.Dispose();
|
_shaderPasses.Dispose();
|
||||||
|
_passLookup.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||||
private readonly CommandBufferType _type;
|
private readonly CommandBufferType _type;
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
private CommandError _lastError;
|
private CommandError _lastError;
|
||||||
|
#endif
|
||||||
private ushort _commandCount;
|
private ushort _commandCount;
|
||||||
private bool _isRecording;
|
private bool _isRecording;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
@@ -166,10 +168,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
_commandList.Get()->Close();
|
_commandList.Get()->Close();
|
||||||
_isRecording = false;
|
_isRecording = false;
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
if (_lastError.Status != ErrorStatus.None)
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
{
|
{
|
||||||
return Result.Failure($"Command buffer ended with errors at {_lastError.CommandIndex}, command '{_lastError.CommandName}': {_lastError.Status}");
|
return Result.Failure($"Command buffer ended with errors at {_lastError.CommandIndex}, command '{_lastError.CommandName}': {_lastError.Status}");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return Result.Success();
|
return Result.Success();
|
||||||
}
|
}
|
||||||
@@ -178,6 +182,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
|
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
|
||||||
@@ -188,6 +198,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var count = 0u;
|
var count = 0u;
|
||||||
@@ -238,6 +254,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
if (stateBefore == stateAfter)
|
if (stateBefore == stateAfter)
|
||||||
@@ -264,6 +286,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var recordResult = _resourceDatabase.GetResourceRecord(resource);
|
var recordResult = _resourceDatabase.GetResourceRecord(resource);
|
||||||
@@ -290,6 +318,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var pRtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length];
|
var pRtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length];
|
||||||
@@ -337,6 +371,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var pRtvDescs = stackalloc D3D12_RENDER_PASS_RENDER_TARGET_DESC[rtDescs.Length];
|
var pRtvDescs = stackalloc D3D12_RENDER_PASS_RENDER_TARGET_DESC[rtDescs.Length];
|
||||||
@@ -419,6 +459,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
_commandList.Get()->EndRenderPass();
|
_commandList.Get()->EndRenderPass();
|
||||||
@@ -428,6 +474,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
|
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
|
||||||
@@ -438,14 +490,18 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var psor = _pipelineLibrary.GetGraphicsPSO(pipelineKey);
|
var psor = _pipelineLibrary.GetGraphicsPSO(pipelineKey);
|
||||||
if (psor.Error != ErrorStatus.None)
|
if (psor.Error != ErrorStatus.None)
|
||||||
{
|
{
|
||||||
#if DEBUG || GHOST_EDITOR
|
RecordError(nameof(SetPipelineState), psor.Error);
|
||||||
Logger.LogError($"Failed to get graphics pipeline state object for key {pipelineKey}: {psor.Error}");
|
|
||||||
#endif
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,6 +513,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||||
@@ -467,6 +529,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var recordResult = _resourceDatabase.GetResourceRecord(buffer.AsResource());
|
var recordResult = _resourceDatabase.GetResourceRecord(buffer.AsResource());
|
||||||
@@ -491,6 +559,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||||
@@ -508,6 +582,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var d3d12Topology = topology switch
|
var d3d12Topology = topology switch
|
||||||
@@ -525,6 +605,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
|
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
|
||||||
@@ -534,6 +620,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
||||||
@@ -543,6 +635,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||||
@@ -552,6 +650,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||||
@@ -573,6 +677,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var sizeInBytes = (uint)(data.Length * sizeof(T));
|
var sizeInBytes = (uint)(data.Length * sizeof(T));
|
||||||
@@ -599,6 +709,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var resource = _resourceDatabase.GetResource(texture.AsResource());
|
var resource = _resourceDatabase.GetResource(texture.AsResource());
|
||||||
@@ -634,6 +750,12 @@ internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
|||||||
{
|
{
|
||||||
ThrowIfDisposed();
|
ThrowIfDisposed();
|
||||||
ThrowIfNotRecording();
|
ThrowIfNotRecording();
|
||||||
|
#if !DEBUG
|
||||||
|
if (_lastError.Status != ErrorStatus.None)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
IncrementCommandCount();
|
IncrementCommandCount();
|
||||||
|
|
||||||
var pDestResource = _resourceDatabase.GetResource(dest.AsResource());
|
var pDestResource = _resourceDatabase.GetResource(dest.AsResource());
|
||||||
|
|||||||
@@ -233,23 +233,23 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
|||||||
return Result.Success(cbufferInfo);
|
return Result.Success(cbufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTestOptions z) => z switch
|
private static D3D12_COMPARISON_FUNC ToD3DCompare(ZTest z) => z switch
|
||||||
{
|
{
|
||||||
ZTestOptions.Disabled => D3D12_COMPARISON_FUNC_NEVER,
|
ZTest.Disabled => D3D12_COMPARISON_FUNC_NEVER,
|
||||||
ZTestOptions.Less => D3D12_COMPARISON_FUNC_LESS,
|
ZTest.Less => D3D12_COMPARISON_FUNC_LESS,
|
||||||
ZTestOptions.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
ZTest.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
||||||
ZTestOptions.Equal => D3D12_COMPARISON_FUNC_EQUAL,
|
ZTest.Equal => D3D12_COMPARISON_FUNC_EQUAL,
|
||||||
ZTestOptions.GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL,
|
ZTest.GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL,
|
||||||
ZTestOptions.Greater => D3D12_COMPARISON_FUNC_GREATER,
|
ZTest.Greater => D3D12_COMPARISON_FUNC_GREATER,
|
||||||
ZTestOptions.NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL,
|
ZTest.NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL,
|
||||||
ZTestOptions.Always => D3D12_COMPARISON_FUNC_ALWAYS,
|
ZTest.Always => D3D12_COMPARISON_FUNC_ALWAYS,
|
||||||
_ => D3D12_COMPARISON_FUNC_LESS_EQUAL
|
_ => D3D12_COMPARISON_FUNC_LESS_EQUAL
|
||||||
};
|
};
|
||||||
|
|
||||||
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTestOptions ztest, ZWriteOptions zwrite)
|
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTest ztest, ZWrite zwrite)
|
||||||
{
|
{
|
||||||
var depthEnabled = ztest != ZTestOptions.Disabled;
|
var depthEnabled = ztest != ZTest.Disabled;
|
||||||
var writeEnabled = zwrite == ZWriteOptions.On;
|
var writeEnabled = zwrite == ZWrite.On;
|
||||||
var cmp = ToD3DCompare(ztest);
|
var cmp = ToD3DCompare(ztest);
|
||||||
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
|
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
|
||||||
}
|
}
|
||||||
@@ -295,23 +295,16 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
|||||||
return psr.Value;
|
return psr.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = new GraphicsPipelineHash
|
if (descriptor.RtvFormats.Length > D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT)
|
||||||
{
|
{
|
||||||
Id = descriptor.PassId,
|
return Result.Failure($"RTV format count exceeds the maximum supported render target count of {D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT}.");
|
||||||
RtvCount = (uint)descriptor.RtvFormats.Length,
|
|
||||||
DsvFormat = descriptor.DsvFormat,
|
|
||||||
};
|
|
||||||
|
|
||||||
var rtvCount = (uint)Math.Min(descriptor.RtvFormats.Length, D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT);
|
|
||||||
|
|
||||||
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
|
||||||
{
|
|
||||||
hash.RtvFormats[i] = descriptor.RtvFormats[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = hash.GetKey();
|
var passPipelineKey = new PassPipelineKey(descriptor.RtvFormats, descriptor.DsvFormat);
|
||||||
|
var materialPipelineKey = new MaterialPipelineKey(descriptor.PassId, descriptor.PipelineOption);
|
||||||
|
var pipelineKey = GraphicsPipelineKey.Combine(materialPipelineKey, passPipelineKey);
|
||||||
|
|
||||||
if (!_pipelineCache.ContainsKey(key))
|
if (!_pipelineCache.ContainsKey(pipelineKey))
|
||||||
{
|
{
|
||||||
var result = ValidatePassReflectionData(in compiled);
|
var result = ValidatePassReflectionData(in compiled);
|
||||||
if (result.IsFailure)
|
if (result.IsFailure)
|
||||||
@@ -327,26 +320,26 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
|||||||
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
|
||||||
SampleMask = UINT32_MAX,
|
SampleMask = UINT32_MAX,
|
||||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||||
NumRenderTargets = rtvCount,
|
NumRenderTargets = (uint)descriptor.RtvFormats.Length,
|
||||||
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
|
DSVFormat = descriptor.DsvFormat.ToDXGIFormat(),
|
||||||
DepthStencilState = BuildDepthStencil(descriptor.ZTest, descriptor.ZWrite),
|
DepthStencilState = BuildDepthStencil(descriptor.PipelineOption.ZTest, descriptor.PipelineOption.ZWrite),
|
||||||
NodeMask = 0,
|
NodeMask = 0,
|
||||||
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
Flags = D3D12_PIPELINE_STATE_FLAG_NONE,
|
||||||
|
|
||||||
BlendState = descriptor.Blend switch
|
BlendState = descriptor.PipelineOption.Blend switch
|
||||||
{
|
{
|
||||||
BlendOptions.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
|
Blend.Opaque => D3D12Utility.D3D12_BLEND_DESC_OPAQUE,
|
||||||
BlendOptions.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
|
Blend.Alpha => D3D12Utility.D3D12_BLEND_DESC_ALPHA_BLEND,
|
||||||
BlendOptions.Additive => D3D12Utility.D3D12_BLEND_DESC_ADDITIVE,
|
Blend.Additive => D3D12Utility.D3D12_BLEND_DESC_ADDITIVE,
|
||||||
BlendOptions.Multiply => D3D12Utility.D3D12_BLEND_DESC_MULTIPLY,
|
Blend.Multiply => D3D12Utility.D3D12_BLEND_DESC_MULTIPLY,
|
||||||
BlendOptions.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
|
Blend.PremultipliedAlpha => D3D12Utility.D3D12_BLEND_DESC_PREMULTIPLIED,
|
||||||
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
|
_ => D3D12Utility.D3D12_BLEND_DESC_OPAQUE
|
||||||
},
|
},
|
||||||
RasterizerState = descriptor.Cull switch
|
RasterizerState = descriptor.PipelineOption.Cull switch
|
||||||
{
|
{
|
||||||
CullOptions.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
|
Cull.Off => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE,
|
||||||
CullOptions.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
|
Cull.Front => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_CLOCKWISE,
|
||||||
CullOptions.Back => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE,
|
Cull.Back => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE,
|
||||||
_ => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE
|
_ => D3D12Utility.D3D12_RASTERIZER_DESC_CULL_NONE
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -356,10 +349,10 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
|||||||
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
desc.AS = new D3D12_SHADER_BYTECODE(compiled.tsResult.bytecode.GetUnsafePtr(), (nuint)compiled.tsResult.bytecode.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < rtvCount && i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
for (var i = 0; i < descriptor.RtvFormats.Length; i++)
|
||||||
{
|
{
|
||||||
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
desc.RTVFormats[i] = descriptor.RtvFormats[i].ToDXGIFormat();
|
||||||
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)(descriptor.ColorMask & 0x0F);
|
desc.BlendState.RenderTarget[i].RenderTargetWriteMask = (byte)((int)descriptor.PipelineOption.ColorMask & 0x0F);
|
||||||
}
|
}
|
||||||
|
|
||||||
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
|
var meshStream = new CD3DX12_PIPELINE_MESH_STATE_STREAM(in desc);
|
||||||
@@ -373,7 +366,7 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
|||||||
|
|
||||||
var pKeyStr = stackalloc char[GraphicsPipelineKey.KEY_STRING_LENGTH];
|
var pKeyStr = stackalloc char[GraphicsPipelineKey.KEY_STRING_LENGTH];
|
||||||
var keySpan = new Span<char>(pKeyStr, GraphicsPipelineKey.KEY_STRING_LENGTH);
|
var keySpan = new Span<char>(pKeyStr, GraphicsPipelineKey.KEY_STRING_LENGTH);
|
||||||
var kr = key.GetString(keySpan);
|
var kr = pipelineKey.GetString(keySpan);
|
||||||
if (kr.IsFailure)
|
if (kr.IsFailure)
|
||||||
{
|
{
|
||||||
return kr;
|
return kr;
|
||||||
@@ -396,10 +389,15 @@ internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
|||||||
pso.psoDesc = desc;
|
pso.psoDesc = desc;
|
||||||
pso.pso.Attach(pPipelineState);
|
pso.pso.Attach(pPipelineState);
|
||||||
|
|
||||||
_pipelineCache[key] = pso;
|
_pipelineCache[pipelineKey] = pso;
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
return pipelineKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasPipeline(GraphicsPipelineKey key)
|
||||||
|
{
|
||||||
|
return _pipelineCache.ContainsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result<SharedPtr<ID3D12PipelineState>, ErrorStatus> GetGraphicsPSO(GraphicsPipelineKey key)
|
public Result<SharedPtr<ID3D12PipelineState>, ErrorStatus> GetGraphicsPSO(GraphicsPipelineKey key)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ internal class D3D12Renderer : IRenderer
|
|||||||
// NOTE: Testing only.
|
// NOTE: Testing only.
|
||||||
private readonly MeshRenderPass _pass;
|
private readonly MeshRenderPass _pass;
|
||||||
|
|
||||||
public IRenderTargetStrategy? RenderTargetStrategy
|
public IRenderOutput? RenderOutput
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
@@ -47,31 +47,31 @@ internal class D3D12Renderer : IRenderer
|
|||||||
|
|
||||||
public Result Render(ICommandAllocator commandAllocator)
|
public Result Render(ICommandAllocator commandAllocator)
|
||||||
{
|
{
|
||||||
if (RenderTargetStrategy is null)
|
if (RenderOutput is null)
|
||||||
{
|
{
|
||||||
return Result.Failure("Render target strategy is not set.");
|
return Result.Failure("Render target strategy is not set.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var target = RenderTargetStrategy.GetRenderTarget();
|
var target = RenderOutput.GetRenderTarget();
|
||||||
if (target.IsInvalid)
|
if (target.IsInvalid)
|
||||||
{
|
{
|
||||||
return Result.Failure("Render target is invalid.");
|
return Result.Failure("Render target is invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_commandBuffer.Begin(commandAllocator);
|
_commandBuffer.Begin(commandAllocator);
|
||||||
RenderTargetStrategy.BeginRender(_commandBuffer);
|
RenderOutput.BeginRender(_commandBuffer);
|
||||||
|
|
||||||
// NOTE: Temperary solution: render directly to the swap chain back buffer if available.
|
// NOTE: Temperary solution: render directly to the swap chain back buffer if available.
|
||||||
// HACK: This is hard coded for testing purposes only.
|
// HACK: This is hard coded for testing purposes only.
|
||||||
|
|
||||||
var error = RenderScene(target, RenderTargetStrategy.Viewport, RenderTargetStrategy.Scissor);
|
var error = RenderScene(target, RenderOutput.Viewport, RenderOutput.Scissor);
|
||||||
if (error != ErrorStatus.None)
|
if (error != ErrorStatus.None)
|
||||||
{
|
{
|
||||||
_commandBuffer.End();
|
_commandBuffer.End();
|
||||||
return Result.Failure(error);
|
return Result.Failure(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderTargetStrategy.EndRender(_commandBuffer);
|
RenderOutput.EndRender(_commandBuffer);
|
||||||
var r = _commandBuffer.End();
|
var r = _commandBuffer.End();
|
||||||
if (r.IsFailure)
|
if (r.IsFailure)
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,7 @@ internal class D3D12Renderer : IRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
|
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
|
||||||
RenderTargetStrategy.Present();
|
RenderOutput.Present();
|
||||||
|
|
||||||
return Result.Success();
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,8 +100,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
private UnsafeHashMap<SamplerDesc, Identifier<Sampler>> _samplers;
|
private UnsafeHashMap<SamplerDesc, Identifier<Sampler>> _samplers;
|
||||||
private UnsafeSlotMap<Mesh> _meshes;
|
private UnsafeSlotMap<Mesh> _meshes;
|
||||||
private UnsafeSlotMap<Material> _materials;
|
private UnsafeSlotMap<Material> _materials;
|
||||||
private readonly DynamicArray<Shader?> _shaders; // NOTE: We use a simple list since shader is not frequently added/removed. This can save 4 bytes for each ecs component.
|
private readonly DynamicArray<Shader> _shaders; // TODO: Use SlotMap?
|
||||||
// private UnsafeHashMap<ShaderPassKey, ShaderPass> _shaderPasses; // NOTE: The reason we use Dictionary here is that ShaderPassKey is a presistence identifier across multiple application sessions.
|
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
@@ -116,7 +115,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
_samplers = new UnsafeHashMap<SamplerDesc, Identifier<Sampler>>(32, Allocator.Persistent);
|
_samplers = new UnsafeHashMap<SamplerDesc, Identifier<Sampler>>(32, Allocator.Persistent);
|
||||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent, AllocationOption.Clear);
|
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
|
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
|
||||||
_shaders = new DynamicArray<Shader?>(16);
|
_shaders = new DynamicArray<Shader>(16);
|
||||||
// _shaderPasses = new UnsafeHashMap<ShaderPassKey, ShaderPass>(32, Allocator.Persistent);
|
// _shaderPasses = new UnsafeHashMap<ShaderPassKey, ShaderPass>(32, Allocator.Persistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,10 +409,10 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
public bool HasShader(Identifier<Shader> id)
|
public bool HasShader(Identifier<Shader> id)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
return id.Value >= 0 && id.Value < _shaders.Count && _shaders[id.Value] != null;
|
return id.Value >= 0 && id.Value < _shaders.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Shader GetShaderReference(Identifier<Shader> id)
|
public ref Shader GetShaderReference(Identifier<Shader> id)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
@@ -422,8 +421,7 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
throw new ArgumentOutOfRangeException(nameof(id), $"Shader id {id} is invalid.");
|
throw new ArgumentOutOfRangeException(nameof(id), $"Shader id {id} is invalid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var shader = _shaders[id.Value]!;
|
return ref _shaders[id.Value];
|
||||||
return shader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReleaseShader(Identifier<Shader> id)
|
public void ReleaseShader(Identifier<Shader> id)
|
||||||
@@ -470,11 +468,6 @@ internal class D3D12ResourceDatabase : IResourceDatabase
|
|||||||
for (var i = 0; i < _shaders.Count; i++)
|
for (var i = 0; i < _shaders.Count; i++)
|
||||||
{
|
{
|
||||||
ref var shader = ref _shaders[i];
|
ref var shader = ref _shaders[i];
|
||||||
if (shader == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseResource(ref shader);
|
ReleaseResource(ref shader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,17 +55,47 @@ public readonly struct ShaderPassKey : IEquatable<ShaderPassKey>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public readonly struct GraphicsPipelineKey
|
public readonly struct GraphicsPipelineKey
|
||||||
{
|
{
|
||||||
public const int KEY_STRING_LENGTH = 17; // 16 chars + null terminator
|
public const int KEY_STRING_LENGTH = 17; // 16 chars + null terminator
|
||||||
|
|
||||||
public readonly ulong value;
|
public readonly UInt128 value;
|
||||||
|
|
||||||
public GraphicsPipelineKey(ulong value)
|
public GraphicsPipelineKey(UInt128 value)
|
||||||
{
|
{
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static GraphicsPipelineKey Combine(MaterialPipelineKey materialKey, PassPipelineKey passKey)
|
||||||
|
{
|
||||||
|
// Order-sensitive 128-bit mix. Cheap and stable, avoids span hashing.
|
||||||
|
static ulong Mix64(ulong x)
|
||||||
|
{
|
||||||
|
x ^= x >> 30;
|
||||||
|
x *= 0xBF58476D1CE4E5B9ul;
|
||||||
|
x ^= x >> 27;
|
||||||
|
x *= 0x94D049BB133111EBul;
|
||||||
|
x ^= x >> 31;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe static ulong GetLow(UInt128 value) => ((ulong*)&value)[0];
|
||||||
|
unsafe static ulong GetHigh(UInt128 value) => ((ulong*)&value)[1];
|
||||||
|
|
||||||
|
var mLo = GetLow(materialKey.value);
|
||||||
|
var mHi = GetHigh(materialKey.value);
|
||||||
|
var pLo = GetLow(passKey.value);
|
||||||
|
var pHi = GetHigh(passKey.value);
|
||||||
|
|
||||||
|
// Distinct constants + cross-feeding to reduce structural collisions.
|
||||||
|
var lo = Mix64(mLo ^ (pLo + 0x9E3779B97F4A7C15ul) ^ (mHi * 0xD6E8FEB86659FD93ul));
|
||||||
|
var hi = Mix64(mHi ^ (pHi + 0xC2B2AE3D27D4EB4Ful) ^ (pLo * 0x165667B19E3779F9ul));
|
||||||
|
|
||||||
|
return new GraphicsPipelineKey(new UInt128(lo, hi));
|
||||||
|
}
|
||||||
|
|
||||||
public Result GetString(Span<char> destination)
|
public Result GetString(Span<char> destination)
|
||||||
{
|
{
|
||||||
if (!value.TryFormat(destination, out _, "X16"))
|
if (!value.TryFormat(destination, out _, "X16"))
|
||||||
@@ -88,49 +118,70 @@ public readonly struct GraphicsPipelineKey
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct GraphicsPipelineHash
|
public readonly struct MaterialPipelineKey : IEquatable<MaterialPipelineKey>
|
||||||
{
|
{
|
||||||
[InlineArray(D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT)]
|
public readonly UInt128 value;
|
||||||
public struct rtv_array
|
|
||||||
{
|
|
||||||
public TextureFormat rtvFormats;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderPassKey Id
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public rtv_array RtvFormats;
|
|
||||||
|
|
||||||
public uint RtvCount
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureFormat DsvFormat
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do we need to store blend state?
|
|
||||||
// TODO: Variants
|
// TODO: Variants
|
||||||
|
|
||||||
public readonly GraphicsPipelineKey GetKey()
|
public MaterialPipelineKey(ShaderPassKey passKey, PipelineState psoOptions)
|
||||||
{
|
{
|
||||||
Span<ulong> data = stackalloc ulong[3 + D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
|
// 32-bit packed key for states controlled by material / overrides.
|
||||||
data[0] = Id.value;
|
// layout:
|
||||||
data[1] = RtvCount;
|
// 0..3 Blend (4 bits)
|
||||||
data[2] = (ulong)DsvFormat;
|
// 4..6 Cull (3 bits)
|
||||||
|
// 7..10 DeafaultState (4 bits)
|
||||||
|
// 11 ZWrite (1 bit)
|
||||||
|
// 12..15 ColorMask (4 bits)
|
||||||
|
|
||||||
for (var i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
var key = 0u;
|
||||||
{
|
key |= ((uint)psoOptions.Blend & 0xFu) << 0;
|
||||||
data[3 + i] = (ulong)RtvFormats[i];
|
key |= ((uint)psoOptions.Cull & 0x7u) << 4;
|
||||||
|
key |= ((uint)psoOptions.ZTest & 0xFu) << 7;
|
||||||
|
key |= ((uint)psoOptions.ZWrite & 0x1u) << 11;
|
||||||
|
key |= ((uint)psoOptions.ColorMask & 0xFu) << 12;
|
||||||
|
|
||||||
|
value = new UInt128(passKey.value, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
var bytes = MemoryMarshal.AsBytes(data);
|
public bool Equals(MaterialPipelineKey other) => value == other.value;
|
||||||
return new GraphicsPipelineKey(XxHash3.HashToUInt64(bytes));
|
public override bool Equals(object? obj) => obj is MaterialPipelineKey other && Equals(other);
|
||||||
|
public override int GetHashCode() => value.GetHashCode();
|
||||||
|
|
||||||
|
public static bool operator ==(MaterialPipelineKey left, MaterialPipelineKey right) => left.Equals(right);
|
||||||
|
public static bool operator !=(MaterialPipelineKey left, MaterialPipelineKey right) => !(left == right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct PassPipelineKey : IEquatable<PassPipelineKey>
|
||||||
|
{
|
||||||
|
public readonly UInt128 value;
|
||||||
|
|
||||||
|
public PassPipelineKey(ReadOnlySpan<TextureFormat> rtvFormats, TextureFormat dsvFormat)
|
||||||
|
{
|
||||||
|
if (rtvFormats.Length > 8)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"RTV formats length exceeds maximum supported count of {8}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// layout:
|
||||||
|
// 0..64 8 RTV formats (8 bits each)
|
||||||
|
// 64..72 DSV format (8 bits)
|
||||||
|
|
||||||
|
var rtvPart = 0UL;
|
||||||
|
for (var i = 0; i < rtvFormats.Length; i++)
|
||||||
|
{
|
||||||
|
rtvPart |= ((ulong)(byte)rtvFormats[i]) << (i * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = new UInt128(rtvPart, (ulong)dsvFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(PassPipelineKey other) => value == other.value;
|
||||||
|
public override bool Equals(object? obj) => obj is PassPipelineKey other && Equals(other);
|
||||||
|
public override int GetHashCode() => value.GetHashCode();
|
||||||
|
|
||||||
|
public static bool operator ==(PassPipelineKey left, PassPipelineKey right) => left.Equals(right);
|
||||||
|
public static bool operator !=(PassPipelineKey left, PassPipelineKey right) => !(left == right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref struct GraphicsPSODescriptor
|
public ref struct GraphicsPSODescriptor
|
||||||
@@ -140,27 +191,7 @@ public ref struct GraphicsPSODescriptor
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZTestOptions ZTest
|
public PipelineState PipelineOption
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ZWriteOptions ZWrite
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CullOptions Cull
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlendOptions Blend
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint ColorMask
|
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,5 +22,6 @@ public interface IPipelineLibrary : IDisposable
|
|||||||
/// <param name="filePath">File path. If null, load default library.</param>
|
/// <param name="filePath">File path. If null, load default library.</param>
|
||||||
void InitializeLibrary(string? filePath);
|
void InitializeLibrary(string? filePath);
|
||||||
void SaveLibraryToDisk(string filePath);
|
void SaveLibraryToDisk(string filePath);
|
||||||
|
bool HasPipeline(GraphicsPipelineKey key);
|
||||||
Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
Result<GraphicsPipelineKey> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Ghost.Graphics.RHI;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRenderer : IDisposable
|
public interface IRenderer : IDisposable
|
||||||
{
|
{
|
||||||
IRenderTargetStrategy? RenderTargetStrategy
|
IRenderOutput? RenderOutput
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public interface IResourceDatabase : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
|
/// <param name="id">The identifier of the shader to retrieve. Must refer to a valid shader.</param>
|
||||||
/// <returns>A reference to the shader corresponding to the specified identifier.</returns>
|
/// <returns>A reference to the shader corresponding to the specified identifier.</returns>
|
||||||
Shader GetShaderReference(Identifier<Shader> id);
|
ref Shader GetShaderReference(Identifier<Shader> id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
|
/// Releases the shader associated with the specified identifier, freeing any resources allocated to it.
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ internal class MeshRenderPass : IRenderPass
|
|||||||
|
|
||||||
private GraphicsCompiledResult[]? _compileResults;
|
private GraphicsCompiledResult[]? _compileResults;
|
||||||
|
|
||||||
|
private Identifier<ShaderPass> _forwardPassID;
|
||||||
|
|
||||||
// Texture file paths for this demo
|
// Texture file paths for this demo
|
||||||
private readonly string[] _textureFiles = [
|
private readonly string[] _textureFiles = [
|
||||||
"C:/Users/Misaki/Downloads/Im/Icon.png",
|
"C:/Users/Misaki/Downloads/Im/Icon.png",
|
||||||
@@ -64,11 +66,7 @@ internal class MeshRenderPass : IRenderPass
|
|||||||
var psoDes = new GraphicsPSODescriptor
|
var psoDes = new GraphicsPSODescriptor
|
||||||
{
|
{
|
||||||
PassId = new ShaderPassKey(fullPass.Identifier),
|
PassId = new ShaderPassKey(fullPass.Identifier),
|
||||||
ZTest = fullPass.localPipeline.zTest,
|
PipelineOption = fullPass.localPipeline,
|
||||||
ZWrite = fullPass.localPipeline.zWrite,
|
|
||||||
Cull = fullPass.localPipeline.cull,
|
|
||||||
Blend = fullPass.localPipeline.blend,
|
|
||||||
ColorMask = fullPass.localPipeline.colorMask,
|
|
||||||
|
|
||||||
RtvFormats = [TextureFormat.B8G8R8A8_UNorm],
|
RtvFormats = [TextureFormat.B8G8R8A8_UNorm],
|
||||||
DsvFormat = TextureFormat.Unknown,
|
DsvFormat = TextureFormat.Unknown,
|
||||||
@@ -103,7 +101,7 @@ internal class MeshRenderPass : IRenderPass
|
|||||||
Usage = TextureUsage.ShaderResource,
|
Usage = TextureUsage.ShaderResource,
|
||||||
};
|
};
|
||||||
|
|
||||||
_textures[i] = ctx.CreateTexture(ref desc, imageData.AsSpan());
|
_textures[i] = ctx.CreateTexture(in desc, imageData.AsSpan());
|
||||||
}
|
}
|
||||||
|
|
||||||
var samplerDesc = new SamplerDesc
|
var samplerDesc = new SamplerDesc
|
||||||
@@ -130,11 +128,13 @@ internal class MeshRenderPass : IRenderPass
|
|||||||
|
|
||||||
Debug.Assert(matRef.SetPropertyCache(in matProps) == ErrorStatus.None);
|
Debug.Assert(matRef.SetPropertyCache(in matProps) == ErrorStatus.None);
|
||||||
matRef.UploadData(ctx.DirectCommandBuffer);
|
matRef.UploadData(ctx.DirectCommandBuffer);
|
||||||
|
|
||||||
|
_forwardPassID = Shader.GetPassID("Forward");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ref readonly RenderingContext ctx)
|
public void Execute(ref readonly RenderingContext ctx)
|
||||||
{
|
{
|
||||||
ctx.DispatchMesh(_mesh, _material, "Forward", 3);
|
ctx.DispatchMesh(_mesh, _material, _forwardPassID, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Cleanup(IResourceDatabase resourceDatabase)
|
public void Cleanup(IResourceDatabase resourceDatabase)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ shader "MyShader/Standard"
|
|||||||
zwrite = off;
|
zwrite = off;
|
||||||
cull = off;
|
cull = off;
|
||||||
blend = opaque;
|
blend = opaque;
|
||||||
color_mask = 15;
|
color_mask = all;
|
||||||
}
|
}
|
||||||
|
|
||||||
ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain");
|
ms("F:/csharp/GhostEngine/Ghost.Graphics/RenderPasses/ShaderCode.hlsl", "MSMain");
|
||||||
@@ -5,7 +5,7 @@ using System.Numerics;
|
|||||||
//ShaderStructGenerator.GenerateHLSL([typeof(TestStruct), typeof(TestEnum), typeof(TestEnumFlags)], PackingRules.Exact, "C:/Users/Misaki/Downloads/Archive/Test.cs.hlsl");
|
//ShaderStructGenerator.GenerateHLSL([typeof(TestStruct), typeof(TestEnum), typeof(TestEnumFlags)], PackingRules.Exact, "C:/Users/Misaki/Downloads/Archive/Test.cs.hlsl");
|
||||||
|
|
||||||
//return;
|
//return;
|
||||||
|
#if false
|
||||||
var source = File.ReadAllText("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader");
|
var source = File.ReadAllText("F:/csharp/GhostEngine/Ghost.Graphics/test.gshader");
|
||||||
|
|
||||||
var lexer = new Lexer(source);
|
var lexer = new Lexer(source);
|
||||||
@@ -33,7 +33,7 @@ var descriptor = SDLCompiler.ResolveShader(model);
|
|||||||
SDLCompiler.GenerateShaderCode(descriptor, "C:/Users/Misaki/Downloads/Archive");
|
SDLCompiler.GenerateShaderCode(descriptor, "C:/Users/Misaki/Downloads/Archive");
|
||||||
|
|
||||||
Console.WriteLine("Shader compiled successfully:");
|
Console.WriteLine("Shader compiled successfully:");
|
||||||
|
#endif
|
||||||
|
|
||||||
public struct TestStruct
|
public struct TestStruct
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,117 +53,71 @@ internal class PipelineBlock : IBlockParser<PipelineSyntax, PipelineSemantic>
|
|||||||
switch (valueDecl.name.lexeme)
|
switch (valueDecl.name.lexeme)
|
||||||
{
|
{
|
||||||
case TokenLexicon.KnownPipelineProperties.ZTEST:
|
case TokenLexicon.KnownPipelineProperties.ZTEST:
|
||||||
switch (valueDecl.value.lexeme)
|
if (Enum.TryParse<ZTest>(valueDecl.value.lexeme, true, out var zTest))
|
||||||
|
{
|
||||||
|
semantic.zTest = zTest;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
case "disable":
|
|
||||||
semantic.zTest = ZTestOptions.Disabled;
|
|
||||||
break;
|
|
||||||
case "less":
|
|
||||||
semantic.zTest = ZTestOptions.Less;
|
|
||||||
break;
|
|
||||||
case "less_equal":
|
|
||||||
semantic.zTest = ZTestOptions.LessEqual;
|
|
||||||
break;
|
|
||||||
case "equal":
|
|
||||||
semantic.zTest = ZTestOptions.Equal;
|
|
||||||
break;
|
|
||||||
case "greater_equal":
|
|
||||||
semantic.zTest = ZTestOptions.GreaterEqual;
|
|
||||||
break;
|
|
||||||
case "greater":
|
|
||||||
semantic.zTest = ZTestOptions.Greater;
|
|
||||||
break;
|
|
||||||
case "not_equal":
|
|
||||||
semantic.zTest = ZTestOptions.NotEqual;
|
|
||||||
break;
|
|
||||||
case "always":
|
|
||||||
semantic.zTest = ZTestOptions.Always;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errors.Add(new SDLError
|
errors.Add(new SDLError
|
||||||
{
|
{
|
||||||
message = $"Invalid ZTest option: {valueDecl.value.lexeme}",
|
message = $"Invalid ZTest option: {valueDecl.value.lexeme}",
|
||||||
line = valueDecl.value.line,
|
line = valueDecl.value.line,
|
||||||
column = valueDecl.value.column
|
column = valueDecl.value.column
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenLexicon.KnownPipelineProperties.ZWRITE:
|
case TokenLexicon.KnownPipelineProperties.ZWRITE:
|
||||||
switch (valueDecl.value.lexeme)
|
if (Enum.TryParse<ZWrite>(valueDecl.value.lexeme, true, out var zWrite))
|
||||||
|
{
|
||||||
|
semantic.zWrite = zWrite;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
case "on":
|
|
||||||
semantic.zWrite = ZWriteOptions.On;
|
|
||||||
break;
|
|
||||||
case "off":
|
|
||||||
semantic.zWrite = ZWriteOptions.Off;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errors.Add(new SDLError
|
errors.Add(new SDLError
|
||||||
{
|
{
|
||||||
message = $"Invalid ZWrite option: {valueDecl.value.lexeme}",
|
message = $"Invalid ZWrite option: {valueDecl.value.lexeme}",
|
||||||
line = valueDecl.value.line,
|
line = valueDecl.value.line,
|
||||||
column = valueDecl.value.column
|
column = valueDecl.value.column
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenLexicon.KnownPipelineProperties.CULL:
|
case TokenLexicon.KnownPipelineProperties.CULL:
|
||||||
switch (valueDecl.value.lexeme)
|
if (Enum.TryParse<Cull>(valueDecl.value.lexeme, true, out var cull))
|
||||||
|
{
|
||||||
|
semantic.cull = cull;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
case "off":
|
|
||||||
semantic.cull = CullOptions.Off;
|
|
||||||
break;
|
|
||||||
case "front":
|
|
||||||
semantic.cull = CullOptions.Front;
|
|
||||||
break;
|
|
||||||
case "back":
|
|
||||||
semantic.cull = CullOptions.Back;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errors.Add(new SDLError
|
errors.Add(new SDLError
|
||||||
{
|
{
|
||||||
message = $"Invalid Cull option: {valueDecl.value.lexeme}",
|
message = $"Invalid Cull option: {valueDecl.value.lexeme}",
|
||||||
line = valueDecl.value.line,
|
line = valueDecl.value.line,
|
||||||
column = valueDecl.value.column
|
column = valueDecl.value.column
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenLexicon.KnownPipelineProperties.BLEND:
|
case TokenLexicon.KnownPipelineProperties.BLEND:
|
||||||
switch (valueDecl.value.lexeme)
|
if (Enum.TryParse<Blend>(valueDecl.value.lexeme, true, out var blend))
|
||||||
|
{
|
||||||
|
semantic.blend = blend;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
case "opaque":
|
|
||||||
semantic.blend = BlendOptions.Opaque;
|
|
||||||
break;
|
|
||||||
case "alpha":
|
|
||||||
semantic.blend = BlendOptions.Alpha;
|
|
||||||
break;
|
|
||||||
case "additive":
|
|
||||||
semantic.blend = BlendOptions.Additive;
|
|
||||||
break;
|
|
||||||
case "multiply":
|
|
||||||
semantic.blend = BlendOptions.Multiply;
|
|
||||||
break;
|
|
||||||
case "premultiplied":
|
|
||||||
semantic.blend = BlendOptions.PremultipliedAlpha;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errors.Add(new SDLError
|
errors.Add(new SDLError
|
||||||
{
|
{
|
||||||
message = $"Invalid Blend option: {valueDecl.value.lexeme}",
|
message = $"Invalid Blend option: {valueDecl.value.lexeme}",
|
||||||
line = valueDecl.value.line,
|
line = valueDecl.value.line,
|
||||||
column = valueDecl.value.column
|
column = valueDecl.value.column
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TokenLexicon.KnownPipelineProperties.COLORMASK:
|
case TokenLexicon.KnownPipelineProperties.COLORMASK:
|
||||||
if (uint.TryParse(valueDecl.value.lexeme, out var colorMask))
|
if (Enum.TryParse<ColorWriteMask>(valueDecl.value.lexeme, true, out var colorMask))
|
||||||
{
|
{
|
||||||
semantic.colorMask = colorMask;
|
semantic.colorMask = colorMask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,20 +128,20 @@ internal static class SDLCompiler
|
|||||||
return $"{shader.name}_{pass.name}";
|
return $"{shader.name}_{pass.name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PipelineDescriptor MeragePipeline(PipelineSemantic? semantic, PipelineDescriptor parent)
|
private static PipelineState MeragePipeline(PipelineSemantic? semantic, PipelineState parent)
|
||||||
{
|
{
|
||||||
if (semantic == null)
|
if (semantic == null)
|
||||||
{
|
{
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PipelineDescriptor
|
return new PipelineState
|
||||||
{
|
{
|
||||||
zTest = semantic.zTest ?? parent.zTest,
|
ZTest = semantic.zTest ?? parent.ZTest,
|
||||||
zWrite = semantic.zWrite ?? parent.zWrite,
|
ZWrite = semantic.zWrite ?? parent.ZWrite,
|
||||||
cull = semantic.cull ?? parent.cull,
|
Cull = semantic.cull ?? parent.Cull,
|
||||||
blend = semantic.blend ?? parent.blend,
|
Blend = semantic.blend ?? parent.Blend,
|
||||||
colorMask = semantic.colorMask ?? parent.colorMask
|
ColorMask = semantic.colorMask ?? parent.ColorMask
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ internal static class SDLCompiler
|
|||||||
{
|
{
|
||||||
foreach (var pass in semantics.passes)
|
foreach (var pass in semantics.passes)
|
||||||
{
|
{
|
||||||
var localPipeline = MeragePipeline(pass.localPipeline, PipelineDescriptor.Default);
|
var localPipeline = MeragePipeline(pass.localPipeline, PipelineState.Default);
|
||||||
var fullPass = new FullPassDescriptor
|
var fullPass = new FullPassDescriptor
|
||||||
{
|
{
|
||||||
uniqueIdentifier = GetPassUniqueId(semantics, pass),
|
uniqueIdentifier = GetPassUniqueId(semantics, pass),
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ internal class PropertySemantic
|
|||||||
|
|
||||||
internal class PipelineSemantic
|
internal class PipelineSemantic
|
||||||
{
|
{
|
||||||
public ZTestOptions? zTest;
|
public ZTest? zTest;
|
||||||
public ZWriteOptions? zWrite;
|
public ZWrite? zWrite;
|
||||||
public CullOptions? cull;
|
public Cull? cull;
|
||||||
public BlendOptions? blend;
|
public Blend? blend;
|
||||||
public uint? colorMask;
|
public ColorWriteMask? colorMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class PassSemantic
|
internal class PassSemantic
|
||||||
|
|||||||
Reference in New Issue
Block a user