Refactor rendering projects
This commit is contained in:
@@ -1,18 +1,10 @@
|
||||
global using static TerraFX.Interop.DirectX.D3D12;
|
||||
global using static TerraFX.Interop.DirectX.DirectX;
|
||||
global using static TerraFX.Interop.DirectX.DXGI;
|
||||
global using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
using Ghost.Core.Attributes;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Graphics.Test")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Graphics.Test-Winui")]
|
||||
[assembly: SupportedOSPlatform("windows10.0.19041.0")]
|
||||
|
||||
|
||||
[assembly: EngineAssembly]
|
||||
@@ -1,43 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.Contracts;
|
||||
|
||||
public interface IRenderOutput
|
||||
{
|
||||
ViewportDesc Viewport
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
RectDesc Scissor
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a handle to the current render target texture.
|
||||
/// </summary>
|
||||
/// <returns>A handle to the texture that is currently set as the render target.</returns>
|
||||
Handle<Texture> GetRenderTarget();
|
||||
|
||||
/// <summary>
|
||||
/// Begins a rendering operation using the specified command buffer. Typically this will include resource barriers,
|
||||
/// </summary>
|
||||
/// <param name="cmd">The command buffer that records rendering commands.</param>
|
||||
///
|
||||
void BeginRender(ICommandBuffer cmd);
|
||||
/// <summary>
|
||||
/// Finalizes the rendering process using the specified command buffer.
|
||||
/// </summary>
|
||||
/// <param name="cmd">The command buffer that contains the rendering commands to be finalized.</param>
|
||||
void EndRender(ICommandBuffer cmd);
|
||||
|
||||
/// <summary>
|
||||
/// Displays the current frame to the output device or screen.
|
||||
/// </summary>
|
||||
/// <remarks>Call this method after rendering operations to present the rendered content. The exact
|
||||
/// behavior may depend on the underlying graphics implementation or device.</remarks>
|
||||
void Present();
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.Contracts;
|
||||
namespace Ghost.Graphics.Core.Contracts;
|
||||
|
||||
public interface IRenderPass
|
||||
{
|
||||
void Initialize(ref readonly RenderingContext ctx);
|
||||
void Build(RenderGraph graph, Identifier<RGTexture> backbuffer);
|
||||
void Cleanup(IResourceDatabase resourceDatabase);
|
||||
void Cleanup(IResourceManager resourceManager);
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.Contracts;
|
||||
|
||||
public struct ShaderCompileResult : IDisposable
|
||||
{
|
||||
public UnsafeArray<byte> bytecode;
|
||||
public ShaderReflectionData reflectionData;
|
||||
|
||||
public readonly bool IsCreated => bytecode.IsCreated;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
bytecode.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public struct GraphicsCompiledResult : IDisposable
|
||||
{
|
||||
public ShaderCompileResult tsResult;
|
||||
public ShaderCompileResult msResult;
|
||||
public ShaderCompileResult psResult;
|
||||
|
||||
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 entryPoint;
|
||||
public string? injectedCode;
|
||||
public ShaderStage stage;
|
||||
public CompilerTier tier;
|
||||
public CompilerOptimizeLevel optimizeLevel;
|
||||
public CompilerOption options;
|
||||
}
|
||||
|
||||
public enum CompilerTier
|
||||
{
|
||||
Tier0,
|
||||
Tier1,
|
||||
Tier2
|
||||
}
|
||||
|
||||
public enum CompilerOptimizeLevel
|
||||
{
|
||||
O0,
|
||||
O1,
|
||||
O2,
|
||||
O3
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum CompilerOption
|
||||
{
|
||||
None = 0,
|
||||
KeepDebugInfo = 1 << 0,
|
||||
KeepReflections = 1 << 1,
|
||||
WarnAsError = 1 << 2,
|
||||
SpirvCrossCompile = 1 << 3
|
||||
}
|
||||
|
||||
public enum ShaderStage
|
||||
{
|
||||
TaskShader,
|
||||
MeshShader,
|
||||
PixelShader,
|
||||
ComputeShader
|
||||
}
|
||||
|
||||
public enum ShaderInputType
|
||||
{
|
||||
ConstantBuffer,
|
||||
Texture,
|
||||
Sampler,
|
||||
UAV,
|
||||
StructuredBuffer,
|
||||
ByteAddressBuffer,
|
||||
RWStructuredBuffer,
|
||||
RWByteAddressBuffer
|
||||
}
|
||||
|
||||
public struct ResourceBindingInfo
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ShaderInputType Type
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint BindPoint
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint BindCount
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint Space
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public IReadOnlyList<CBufferPropertyInfo>? Properties
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ShaderReflectionData
|
||||
{
|
||||
public List<ResourceBindingInfo> ResourcesBindings
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ShaderReflectionData()
|
||||
{
|
||||
ResourcesBindings = new List<ResourceBindingInfo>();
|
||||
}
|
||||
}
|
||||
|
||||
public interface IShaderCompiler : IDisposable
|
||||
{
|
||||
Result<ShaderCompileResult> Compile(ref readonly ShaderCompilationConfig config, Allocator allocator);
|
||||
Result<GraphicsCompiledResult> CompilePass(ref readonly PassDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, Key64<ShaderVariant> key);
|
||||
Result<GraphicsCompiledResult, Error> LoadCompiledCache(Key64<ShaderVariant> key);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Graphics.Contracts;
|
||||
|
||||
public unsafe readonly struct ISwapChainPanelNative : ISwapChainPanelNative.Interface, IDisposable
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
internal interface Interface
|
||||
{
|
||||
// IUnknown: QueryInterface, AddRef, Release
|
||||
void QueryInterface(in Guid riid, out IntPtr ppvObject);
|
||||
uint AddRef();
|
||||
uint Release();
|
||||
|
||||
// SetSwapChain is the 4th slot in the vtable (0-based index 3)
|
||||
int SetSwapChain(IntPtr swapChainPtr);
|
||||
}
|
||||
|
||||
private readonly IntPtr _nativePtr;
|
||||
|
||||
public ISwapChainPanelNative(IntPtr nativePtr)
|
||||
{
|
||||
_nativePtr = nativePtr;
|
||||
}
|
||||
|
||||
public void QueryInterface(in Guid riid, out nint ppvObject)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint AddRef()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public uint Release()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ISwapChainPanelNative FromSwapChainPanel(object panel)
|
||||
{
|
||||
// Get the IUnknown/IInspectable pointer
|
||||
var unknown = Marshal.GetIUnknownForObject(panel);
|
||||
try
|
||||
{
|
||||
// Query for ISwapChainPanelNative
|
||||
var iid = typeof(Interface).GUID;
|
||||
var result = Marshal.QueryInterface(unknown, in iid, out var nativePtr);
|
||||
if (result < 0)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(result);
|
||||
}
|
||||
|
||||
return new ISwapChainPanelNative(nativePtr);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.Release(unknown);
|
||||
}
|
||||
}
|
||||
|
||||
public int SetSwapChain(IntPtr swapChainPtr)
|
||||
{
|
||||
var vtbl = *(void***)_nativePtr;
|
||||
var setSwapChainFn = (delegate* unmanaged<IntPtr, IntPtr, int>)vtbl[3];
|
||||
return setSwapChainFn(_nativePtr, swapChainPtr);
|
||||
}
|
||||
|
||||
public void Dispose() => Marshal.Release(_nativePtr);
|
||||
}
|
||||
@@ -1,5 +1,72 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public class Camera
|
||||
{
|
||||
}
|
||||
private readonly IRenderer _renderer;
|
||||
|
||||
private Handle<Texture> _colorTexture;
|
||||
private Handle<Texture> _depthTexture;
|
||||
|
||||
private uint _actualWidth;
|
||||
private uint _actualHeight;
|
||||
|
||||
private uint _virtualWidth;
|
||||
private uint _virtualHeight;
|
||||
|
||||
public IRenderer Renderer => _renderer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the actual width of the camera's render target in pixels. If upscaler is used, this is the width before upscaling.
|
||||
/// </summary>
|
||||
public uint ActualWidth => _actualWidth;
|
||||
/// <summary>
|
||||
/// Gets the actual height of the camera's render target in pixels. If upscaler is used, this is the height before upscaling.
|
||||
/// </summary>
|
||||
public uint ActualHeight => _actualHeight;
|
||||
/// <summary>
|
||||
/// Gets the virtual width of the camera's render target in pixels. If upscaler is used, this is the width after upscaling.
|
||||
/// </summary>
|
||||
public uint VirtualWidth => _virtualWidth;
|
||||
/// <summary>
|
||||
/// Gets the virtual height of the camera's render target in pixels. If upscaler is used, this is the height after upscaling.
|
||||
/// </summary>
|
||||
public uint VirtualHeight => _virtualHeight;
|
||||
|
||||
public RenderGraph? RenderGraph
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Camera(IGraphicsEngine graphicsEngine)
|
||||
{
|
||||
_renderer = graphicsEngine.CreateRenderer();
|
||||
}
|
||||
|
||||
public Camera(IGraphicsEngine graphicsEngine, RenderGraph renderGraph)
|
||||
{
|
||||
_renderer = graphicsEngine.CreateRenderer();
|
||||
RenderGraph = renderGraph;
|
||||
|
||||
_renderer.RenderFunc = DefaultRenderFunc;
|
||||
}
|
||||
|
||||
private Error DefaultRenderFunc(RenderContext context)
|
||||
{
|
||||
if (RenderGraph == null)
|
||||
{
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
RenderGraph.Reset();
|
||||
|
||||
var view = new ViewState(_virtualWidth, _virtualHeight, _actualWidth, _actualHeight);
|
||||
RenderGraph.Compile(in view);
|
||||
RenderGraph.Execute(context.CommandBuffer);
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 4 bytes components.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 4)]
|
||||
public struct Color32 : IEquatable<Color32>
|
||||
{
|
||||
public byte r;
|
||||
public byte g;
|
||||
public byte b;
|
||||
public byte a;
|
||||
|
||||
public Color32(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color32(Color color)
|
||||
: this(color.R, color.G, color.B, color.A)
|
||||
{
|
||||
}
|
||||
|
||||
public Color32(Color128 color128)
|
||||
: this((byte)(color128.r * 255.0f), (byte)(color128.g * 255.0f), (byte)(color128.b * 255.0f), (byte)(color128.a * 255.0f))
|
||||
{
|
||||
}
|
||||
|
||||
public Color32(float4 v)
|
||||
: this((byte)(v.x * 255.0f), (byte)(v.y * 255.0f), (byte)(v.z * 255.0f), (byte)(v.w * 255.0f))
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color32 other)
|
||||
{
|
||||
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color32 color && Equals(color);
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color32 left, Color32 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color32 left, Color32 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 16 bytes components.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct Color128 : IEquatable<Color128>
|
||||
{
|
||||
public float r;
|
||||
public float g;
|
||||
public float b;
|
||||
public float a;
|
||||
|
||||
public Color128(float r, float g, float b, float a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color128(Color color)
|
||||
: this(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public Color128(Color32 color32)
|
||||
: this(color32.r / 255.0f, color32.g / 255.0f, color32.b / 255.0f, color32.a / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public Color128(float4 v)
|
||||
: this(v.x, v.y, v.z, v.w)
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color128 other)
|
||||
{
|
||||
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color128 color && Equals(color);
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color128 left, Color128 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color128 left, Color128 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Vertex
|
||||
{
|
||||
public static class Semantic
|
||||
{
|
||||
public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
public const int COUNT = 5;
|
||||
|
||||
public static readonly FixedText32 Position = new("POSITION");
|
||||
public static readonly FixedText32 Normal = new("NORMAL");
|
||||
public static readonly FixedText32 Tangent = new("TANGENT");
|
||||
public static readonly FixedText32 Uv = new("TEXCOORD");
|
||||
public static readonly FixedText32 Color = new("COLOR");
|
||||
}
|
||||
|
||||
public float4 position;
|
||||
public float4 normal;
|
||||
public float4 tangent;
|
||||
public float4 uv;
|
||||
public Color128 color;
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
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, CompilerTier version)
|
||||
{
|
||||
return (stage, version) switch
|
||||
{
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier0) => "as_6_6",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier0) => "ps_6_6",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier0) => "ms_6_6",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier0) => "cs_6_6",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier1) => "as_6_7",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier1) => "ps_6_7",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier1) => "ms_6_7",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier1) => "cs_6_7",
|
||||
(ShaderStage.TaskShader, CompilerTier.Tier2) => "as_6_8",
|
||||
(ShaderStage.PixelShader, CompilerTier.Tier2) => "ps_6_8",
|
||||
(ShaderStage.MeshShader, CompilerTier.Tier2) => "ms_6_8",
|
||||
(ShaderStage.ComputeShader, CompilerTier.Tier2) => "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.tier), // 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.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> GetFinalShaderCode(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 System.Text.StringBuilder();
|
||||
foreach (var includePath in includes)
|
||||
{
|
||||
sb.AppendLine($"#include \"{includePath}\"");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(injectedCode))
|
||||
{
|
||||
sb.AppendLine($"#line 1 \"hlsl_block\"");
|
||||
sb.AppendLine(injectedCode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(shaderCode))
|
||||
{
|
||||
sb.AppendLine($"#line 1 \"{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<Key64<ShaderVariant>, 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<Key64<ShaderVariant>, 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 = GetFinalShaderCode(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
|
||||
};
|
||||
|
||||
var (iid, ppv) = Win32Utility.IID_PPV_ARGS(&result);
|
||||
ThrowIfFailed(_compiler.Get()->Compile(&buffer, argPtrs, (uint)argsArray.Count, includeHandler, iid, ppv));
|
||||
|
||||
// Check compilation result
|
||||
HRESULT hrStatus;
|
||||
result.Get()->GetStatus(&hrStatus);
|
||||
if (hrStatus.FAILED)
|
||||
{
|
||||
// Get error messages
|
||||
IDxcBlobEncoding* pErrorBlob = default;
|
||||
result.Get()->GetErrorBuffer(&pErrorBlob);
|
||||
|
||||
if (pErrorBlob != null)
|
||||
{
|
||||
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;
|
||||
(iid, ppv) = Win32Utility.IID_PPV_ARGS(&reflection);
|
||||
|
||||
if (result.Get()->GetOutput(DXC_OUT_KIND.DXC_OUT_REFLECTION, iid, ppv, null).SUCCEEDED)
|
||||
{
|
||||
reflectionData = PerformDXCReflection(reflection).GetValueOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
for (var i = 0; i < argsArray.Count; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal((nint)argPtrs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be shader variant specific compile instead of pass specific.
|
||||
// TODO: Build final shader code in memory before compiling.
|
||||
public Result<GraphicsCompiledResult> CompilePass(ref readonly PassDescriptor descriptor, ref readonly ShaderCompilationConfig additionalConfig, Key64<ShaderVariant> key)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var defineCountInDescriptor = descriptor.defines?.Length ?? 0;
|
||||
var fullDefines = new string[defineCountInDescriptor + additionalConfig.defines.Length];
|
||||
descriptor.defines?.CopyTo(fullDefines);
|
||||
additionalConfig.defines.CopyTo(fullDefines.AsSpan(defineCountInDescriptor));
|
||||
|
||||
ShaderCompileResult tsResult = default;
|
||||
var tsEntry = descriptor.taskShader;
|
||||
if (tsEntry.IsCreated)
|
||||
{
|
||||
var config = new ShaderCompilationConfig
|
||||
{
|
||||
defines = fullDefines.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
shaderPath = tsEntry.shader,
|
||||
entryPoint = tsEntry.entry,
|
||||
injectedCode = descriptor.hlsl + additionalConfig.injectedCode,
|
||||
stage = ShaderStage.TaskShader,
|
||||
tier = additionalConfig.tier,
|
||||
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.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
shaderPath = msEntry.shader,
|
||||
entryPoint = msEntry.entry,
|
||||
injectedCode = descriptor.hlsl + additionalConfig.injectedCode,
|
||||
stage = ShaderStage.MeshShader,
|
||||
tier = additionalConfig.tier,
|
||||
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.AsSpan(),
|
||||
includes = descriptor.includes.AsSpan(),
|
||||
shaderPath = psEntry.shader,
|
||||
entryPoint = psEntry.entry,
|
||||
injectedCode = descriptor.hlsl + additionalConfig.injectedCode,
|
||||
stage = ShaderStage.PixelShader,
|
||||
tier = additionalConfig.tier,
|
||||
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,
|
||||
};
|
||||
|
||||
_compiledResults[key] = 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);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
using System.Runtime.Intrinsics;
|
||||
using TerraFX.Interop.Windows;
|
||||
using ElementType = uint;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public unsafe struct LocalKeywordSet
|
||||
{
|
||||
private const int _DATA_ARRAY_LENGTH = 4; // 4 * 32 = 128 bits
|
||||
private const int _BITS_PER_ELEMENT = sizeof(ElementType) * 8;
|
||||
|
||||
private fixed ElementType _data[_DATA_ARRAY_LENGTH];
|
||||
|
||||
public void SetKeyword(int localIndex, bool enabled)
|
||||
{
|
||||
var index = localIndex / _BITS_PER_ELEMENT;
|
||||
var bit = localIndex % _BITS_PER_ELEMENT;
|
||||
if (enabled)
|
||||
{
|
||||
_data[index] |= (uint)(1 << bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
_data[index] &= ~(uint)(1 << bit);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsKeywordEnabled(int localIndex)
|
||||
{
|
||||
var index = localIndex / _BITS_PER_ELEMENT;
|
||||
var bit = localIndex % _BITS_PER_ELEMENT;
|
||||
return (_data[index] & (uint)(1 << bit)) != 0;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
_data[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetHash64()
|
||||
{
|
||||
ulong hash = 14695981039346656037ul; // FNV Offset basis
|
||||
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
hash ^= _data[i];
|
||||
hash *= 1099511628211ul; // FNV prime
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hash = 17;
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
hash = hash * 31 + _data[i].GetHashCode();
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
public static LocalKeywordSet operator |(in LocalKeywordSet a, in LocalKeywordSet b)
|
||||
{
|
||||
var result = default(LocalKeywordSet);
|
||||
|
||||
if (Vector128<ElementType>.IsSupported)
|
||||
{
|
||||
fixed (ElementType* pDataA = a._data)
|
||||
fixed (ElementType* pDataB = b._data)
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i += Vector128<ElementType>.Count)
|
||||
{
|
||||
var elementOffset = (nuint)i;
|
||||
var vecA = Vector128.LoadUnsafe(ref *pDataA, elementOffset);
|
||||
var vecB = Vector128.LoadUnsafe(ref *pDataB, elementOffset);
|
||||
var vecResult = Vector128.BitwiseOr(vecA, vecB);
|
||||
vecResult.StoreUnsafe(ref result._data[0], elementOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
result._data[i] = a._data[i] | b._data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LocalKeywordSet operator &(in LocalKeywordSet a, in LocalKeywordSet b)
|
||||
{
|
||||
var result = default(LocalKeywordSet);
|
||||
|
||||
if (Vector128<ElementType>.IsSupported)
|
||||
{
|
||||
fixed (ElementType* pDataA = a._data)
|
||||
fixed (ElementType* pDataB = b._data)
|
||||
{
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i += Vector128<ElementType>.Count)
|
||||
{
|
||||
var elementOffset = (nuint)i;
|
||||
var vecA = Vector128.LoadUnsafe(ref *pDataA, elementOffset);
|
||||
var vecB = Vector128.LoadUnsafe(ref *pDataB, elementOffset);
|
||||
var vecResult = Vector128.BitwiseAnd(vecA, vecB);
|
||||
vecResult.StoreUnsafe(ref result._data[0], elementOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (var i = 0; i < _DATA_ARRAY_LENGTH; i++)
|
||||
{
|
||||
result._data[i] = a._data[i] & b._data[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -65,17 +65,17 @@ public struct Material : IResourceReleasable
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Error SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database)
|
||||
public Error SetShader(Identifier<Shader> shaderId, IResourceManager manager)
|
||||
{
|
||||
if (!shaderId.IsValid)
|
||||
{
|
||||
return Error.InvalidArgument;
|
||||
}
|
||||
|
||||
_cBufferCache.ReleaseResource(database);
|
||||
_cBufferCache.ReleaseResource(manager.ResourceDatabase);
|
||||
_shader = shaderId;
|
||||
|
||||
var r = database.GetShaderReference(shaderId);
|
||||
var r = manager.GetShaderReference(shaderId);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r.Error;
|
||||
@@ -101,7 +101,7 @@ public struct Material : IResourceReleasable
|
||||
_passPipelineOverride[i] = new PipelineOverride
|
||||
{
|
||||
shaderPass = pass.Key,
|
||||
options = pass.DeafaultState,
|
||||
options = pass.DefaultState,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public struct Material : IResourceReleasable
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var buffer = allocator.CreateBuffer(ref desc, "MaterialCBuffer");
|
||||
var buffer = manager.ResourceAllocator.CreateBuffer(ref desc, "MaterialCBuffer");
|
||||
_cBufferCache = new CBufferCache(buffer, shader.CBufferSize);
|
||||
}
|
||||
|
||||
@@ -201,9 +201,9 @@ public struct Material : IResourceReleasable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Error SetKeyword(IResourceDatabase resourceDatabase, int keywordId, bool enabled)
|
||||
public Error SetKeyword(IResourceManager manager, int keywordId, bool enabled)
|
||||
{
|
||||
var r = resourceDatabase.GetShaderReference(_shader);
|
||||
var r = manager.GetShaderReference(_shader);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r.Error;
|
||||
@@ -223,9 +223,9 @@ public struct Material : IResourceReleasable
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool IsKeywordEnabled(IResourceDatabase resourceDatabase, int keywordId)
|
||||
public readonly bool IsKeywordEnabled(IResourceManager manager, int keywordId)
|
||||
{
|
||||
var r = resourceDatabase.GetShaderReference(_shader);
|
||||
var r = manager.GetShaderReference(_shader);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public readonly struct GPUResource;
|
||||
public readonly struct Texture;
|
||||
public readonly struct GraphicsBuffer;
|
||||
|
||||
public readonly struct Sampler;
|
||||
|
||||
public static class ResourceHandleExtensions
|
||||
{
|
||||
public static Handle<GPUResource> AsResource(this Handle<Texture> texture)
|
||||
{
|
||||
return new Handle<GPUResource>(texture.ID, texture.Generation);
|
||||
}
|
||||
|
||||
public static Handle<GPUResource> AsResource(this Handle<GraphicsBuffer> buffer)
|
||||
{
|
||||
return new Handle<GPUResource>(buffer.ID, buffer.Generation);
|
||||
}
|
||||
|
||||
internal static Handle<Texture> AsTexture(this Handle<GPUResource> resource)
|
||||
{
|
||||
return new Handle<Texture>(resource.ID, resource.Generation);
|
||||
}
|
||||
|
||||
internal static Handle<GraphicsBuffer> AsGraphicsBuffer(this Handle<GPUResource> resource)
|
||||
{
|
||||
return new Handle<GraphicsBuffer>(resource.ID, resource.Generation);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
/// <summary>
|
||||
/// The layout of the root signature is:
|
||||
/// <list space="bullet">
|
||||
/// <item>
|
||||
/// Global buffer (b0)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Per-view buffer (b1)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Per-object buffer (b2)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Per-material buffer (b3)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Descriptor table for bindless textures (t0)
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// Descriptor table for bindless samplers (s0)
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public static class RootSignatureLayout
|
||||
{
|
||||
// public const int GLOBAL_BUFFER_SLOT = 0;
|
||||
// public const int PER_VIEW_BUFFER_SLOT = 1;
|
||||
// public const int PER_OBJECT_BUFFER_SLOT = 2;
|
||||
// public const int PER_MATERIAL_BUFFER_SLOT = 3;
|
||||
|
||||
// public const int TEXTURE_HEAP_SLOT = 0;
|
||||
// public const int SAMPLER_HEAP_SLOT = 0;
|
||||
|
||||
public const int PUSH_CONSTANT_SLOT = 0;
|
||||
|
||||
public const int ROOT_PARAMETER_COUNT = 1;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct PushConstantsData
|
||||
{
|
||||
public uint globalIndex;
|
||||
public uint viewIndex;
|
||||
public uint objectIndex;
|
||||
public uint materialIndex;
|
||||
}
|
||||
|
||||
// The size should be 176 bytes (16-byte aligned)
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct PerViewData
|
||||
{
|
||||
public float4x4 viewMatrix;
|
||||
public float4x4 projectionMatrix;
|
||||
public float3 cameraPosition;
|
||||
public float nearClip;
|
||||
public float3 cameraDirection;
|
||||
public float farClip;
|
||||
public float4 screenSize; // xy: size, zw: 1/size
|
||||
};
|
||||
|
||||
// The size should be 96 bytes (16-byte aligned)
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct PerObjectData
|
||||
{
|
||||
public float4x4 localToWorld;
|
||||
public float3 worldBoundsMin;
|
||||
public uint vertexBuffer;
|
||||
public float3 worldBoundsMax;
|
||||
public uint indexBuffer;
|
||||
};
|
||||
@@ -7,24 +7,6 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.Core;
|
||||
|
||||
public readonly struct ShaderPass
|
||||
{
|
||||
public Key64<ShaderPass> Key
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public PipelineState DeafaultState
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public LocalKeywordSet KeywordIDs
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ShaderProperty;
|
||||
|
||||
public partial struct Shader
|
||||
@@ -146,7 +128,7 @@ public partial struct Shader : IResourceReleasable
|
||||
_shaderPasses[i] = new ShaderPass
|
||||
{
|
||||
Key = passKey,
|
||||
DeafaultState = pass.localPipeline,
|
||||
DefaultState = pass.localPipeline,
|
||||
KeywordIDs = keywords,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12CommandAllocator : ICommandAllocator
|
||||
{
|
||||
private UniquePtr<ID3D12CommandAllocator> _allocator;
|
||||
|
||||
public SharedPtr<ID3D12CommandAllocator> NativeAllocator => _allocator.Share();
|
||||
|
||||
public D3D12CommandAllocator(D3D12RenderDevice device, CommandBufferType type)
|
||||
{
|
||||
ID3D12CommandAllocator* pAllocator = default;
|
||||
var commandListType = D3D12Utility.ToCommandListType(type);
|
||||
|
||||
device.NativeDevice.Get()->CreateCommandAllocator(commandListType, __uuidof(pAllocator), (void**)&pAllocator);
|
||||
|
||||
_allocator.Attach(pAllocator);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_allocator.Get()->Reset();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_allocator.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,999 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Utilities;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Aliases.D3D_Alias;
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||
{
|
||||
private UniquePtr<ID3D12GraphicsCommandList10> _commandList;
|
||||
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly CommandBufferType _type;
|
||||
|
||||
#if !DEBUG
|
||||
private CommandError _lastError;
|
||||
#endif
|
||||
private ushort _commandCount;
|
||||
private bool _isRecording;
|
||||
private bool _disposed;
|
||||
|
||||
public SharedPtr<ID3D12GraphicsCommandList10> NativeCommandList => _commandList.Get();
|
||||
|
||||
public CommandBufferType Type => _type;
|
||||
public bool IsEmpty => _commandCount == 0;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
if (field == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
field = value;
|
||||
_commandList.Get()->SetName(value);
|
||||
}
|
||||
} = string.Empty;
|
||||
|
||||
public D3D12CommandBuffer(
|
||||
D3D12RenderDevice device,
|
||||
D3D12PipelineLibrary stateController,
|
||||
D3D12ResourceDatabase resourceDatabase,
|
||||
D3D12ResourceAllocator resourceAllocator,
|
||||
D3D12DescriptorAllocator descriptorAllocator,
|
||||
CommandBufferType type)
|
||||
{
|
||||
_type = type;
|
||||
|
||||
ID3D12GraphicsCommandList10* pCommandList = default;
|
||||
var commandListType = D3D12Utility.ToCommandListType(type);
|
||||
|
||||
device.NativeDevice.Get()->CreateCommandList1(0u, commandListType, D3D12_COMMAND_LIST_FLAG_NONE, __uuidof(pCommandList), (void**)&pCommandList);
|
||||
|
||||
_commandList.Attach(pCommandList);
|
||||
|
||||
_pipelineLibrary = stateController;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_resourceAllocator = resourceAllocator;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
|
||||
_isRecording = false;
|
||||
}
|
||||
|
||||
~D3D12CommandBuffer()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfRecording()
|
||||
{
|
||||
if (_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is already recording");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfNotRecording()
|
||||
{
|
||||
if (!_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is not recording");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void IncrementCommandCount()
|
||||
{
|
||||
_commandCount++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#if DEBUG
|
||||
[System.Diagnostics.CodeAnalysis.DoesNotReturn]
|
||||
private static void RecordError(string cmdName, Error status)
|
||||
#else
|
||||
private void RecordError(string cmdName, Error status)
|
||||
#endif
|
||||
{
|
||||
#if DEBUG
|
||||
throw new InvalidOperationException($"Error at {cmdName} with {status}");
|
||||
#else
|
||||
|
||||
_lastError = new CommandError
|
||||
{
|
||||
CommandName = cmdName,
|
||||
CommandIndex = _commandCount,
|
||||
Status = status
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Begin(ICommandAllocator allocator)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfRecording();
|
||||
|
||||
if (allocator is not D3D12CommandAllocator d3d12Allocator)
|
||||
{
|
||||
throw new ArgumentException("Invalid command allocator type", nameof(allocator));
|
||||
}
|
||||
|
||||
ThrowIfFailed(_commandList.Get()->Reset(d3d12Allocator.NativeAllocator, null));
|
||||
|
||||
if (Type == CommandBufferType.Graphics || Type == CommandBufferType.Compute)
|
||||
{
|
||||
// Set descriptor heaps for bindless resources and samplers
|
||||
|
||||
var heaps = stackalloc ID3D12DescriptorHeap*[2];
|
||||
heaps[0] = _descriptorAllocator.GetCbvSrvUavHeap(); // Bindless resource Heap
|
||||
heaps[1] = _descriptorAllocator.GetSamplerHeap(); // Bindless sampler Heap
|
||||
_commandList.Get()->SetDescriptorHeaps(2, heaps);
|
||||
}
|
||||
|
||||
_commandCount = 0;
|
||||
_isRecording = true;
|
||||
}
|
||||
|
||||
public Result End()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
|
||||
_commandList.Get()->Close();
|
||||
_isRecording = false;
|
||||
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return Result.Failure($"Command buffer ended with errors at {_lastError.CommandIndex}, command '{_lastError.CommandName}': {_lastError.Status}");
|
||||
}
|
||||
#endif
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void SetScissorRect(RectDesc rect)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var d3d12Rect = new RECT((int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom);
|
||||
_commandList.Get()->RSSetScissorRects(1, &d3d12Rect);
|
||||
}
|
||||
|
||||
public void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
if (barrierDescs.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var globalCount = 0;
|
||||
var bufferCount = 0;
|
||||
var textureCount = 0;
|
||||
|
||||
for (var i = 0; i < barrierDescs.Length; i++)
|
||||
{
|
||||
switch (barrierDescs[i].Type)
|
||||
{
|
||||
case BarrierType.Global: globalCount++; break;
|
||||
case BarrierType.Buffer: bufferCount++; break;
|
||||
case BarrierType.Texture: textureCount++; break;
|
||||
}
|
||||
}
|
||||
|
||||
var pGlobalBarriers = stackalloc D3D12_GLOBAL_BARRIER[globalCount];
|
||||
var pBufferBarriers = stackalloc D3D12_BUFFER_BARRIER[bufferCount];
|
||||
var pTextureBarriers = stackalloc D3D12_TEXTURE_BARRIER[textureCount];
|
||||
|
||||
var globalIndex = 0;
|
||||
var bufferIndex = 0;
|
||||
var textureIndex = 0;
|
||||
|
||||
for (var i = 0; i < barrierDescs.Length; i++)
|
||||
{
|
||||
var desc = barrierDescs[i];
|
||||
switch (desc.Type)
|
||||
{
|
||||
case BarrierType.Global:
|
||||
pGlobalBarriers[globalIndex++] = new D3D12_GLOBAL_BARRIER
|
||||
{
|
||||
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
|
||||
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
|
||||
AccessBefore = (D3D12_BARRIER_ACCESS)desc.AccessBefore,
|
||||
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter
|
||||
};
|
||||
break;
|
||||
case BarrierType.Buffer:
|
||||
{
|
||||
var r = _resourceDatabase.GetResourceRecord(desc.Resource);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
RecordError(nameof(ResourceBarrier), r.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
ref var record = ref r.Value;
|
||||
var resource = record.ResourcePtr;
|
||||
pBufferBarriers[bufferIndex++] = new D3D12_BUFFER_BARRIER
|
||||
{
|
||||
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
|
||||
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
|
||||
AccessBefore = (D3D12_BARRIER_ACCESS)desc.AccessBefore,
|
||||
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter,
|
||||
pResource = resource,
|
||||
Offset = 0,
|
||||
Size = ulong.MaxValue
|
||||
};
|
||||
|
||||
record.barrierData = new ResourceBarrierData(BarrierLayout.Undefined, desc.AccessAfter, desc.SyncAfter);
|
||||
}
|
||||
break;
|
||||
case BarrierType.Texture:
|
||||
{
|
||||
var r = _resourceDatabase.GetResourceRecord(desc.Resource);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
RecordError(nameof(ResourceBarrier), r.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
ref var record = ref r.Value;
|
||||
var resource = record.ResourcePtr;
|
||||
pTextureBarriers[textureIndex++] = new D3D12_TEXTURE_BARRIER
|
||||
{
|
||||
SyncBefore = (D3D12_BARRIER_SYNC)desc.SyncBefore,
|
||||
SyncAfter = (D3D12_BARRIER_SYNC)desc.SyncAfter,
|
||||
AccessBefore = (D3D12_BARRIER_ACCESS)desc.AccessBefore,
|
||||
AccessAfter = (D3D12_BARRIER_ACCESS)desc.AccessAfter,
|
||||
LayoutBefore = (D3D12_BARRIER_LAYOUT)desc.LayoutBefore,
|
||||
LayoutAfter = (D3D12_BARRIER_LAYOUT)desc.LayoutAfter,
|
||||
pResource = resource,
|
||||
Subresources = new D3D12_BARRIER_SUBRESOURCE_RANGE
|
||||
{
|
||||
IndexOrFirstMipLevel = desc.Subresources.IndexOrFirstMipLevel,
|
||||
NumMipLevels = desc.Subresources.NumMipLevels,
|
||||
FirstArraySlice = desc.Subresources.FirstArraySlice,
|
||||
NumArraySlices = desc.Subresources.NumArraySlices
|
||||
},
|
||||
Flags = desc.Discard ? D3D12_TEXTURE_BARRIER_FLAGS.D3D12_TEXTURE_BARRIER_FLAG_DISCARD : D3D12_TEXTURE_BARRIER_FLAGS.D3D12_TEXTURE_BARRIER_FLAG_NONE
|
||||
};
|
||||
|
||||
record.barrierData = new ResourceBarrierData(desc.LayoutAfter, desc.AccessAfter, desc.SyncAfter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var groups = stackalloc D3D12_BARRIER_GROUP[3];
|
||||
var groupCount = 0u;
|
||||
|
||||
if (globalCount > 0)
|
||||
{
|
||||
groups[groupCount] = new D3D12_BARRIER_GROUP
|
||||
{
|
||||
Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_GLOBAL,
|
||||
NumBarriers = (uint)globalCount,
|
||||
};
|
||||
groups[groupCount].Anonymous.pGlobalBarriers = pGlobalBarriers;
|
||||
groupCount++;
|
||||
}
|
||||
|
||||
if (bufferCount > 0)
|
||||
{
|
||||
groups[groupCount] = new D3D12_BARRIER_GROUP
|
||||
{
|
||||
Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_BUFFER,
|
||||
NumBarriers = (uint)bufferCount,
|
||||
};
|
||||
groups[groupCount].Anonymous.pBufferBarriers = pBufferBarriers;
|
||||
groupCount++;
|
||||
}
|
||||
|
||||
if (textureCount > 0)
|
||||
{
|
||||
groups[groupCount] = new D3D12_BARRIER_GROUP
|
||||
{
|
||||
Type = D3D12_BARRIER_TYPE.D3D12_BARRIER_TYPE_TEXTURE,
|
||||
NumBarriers = (uint)textureCount,
|
||||
};
|
||||
groups[groupCount].Anonymous.pTextureBarriers = pTextureBarriers;
|
||||
groupCount++;
|
||||
}
|
||||
|
||||
_commandList.Get()->Barrier(groupCount, groups);
|
||||
}
|
||||
|
||||
public void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var pRtvHandles = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[renderTargets.Length];
|
||||
var rtvCount = 0u;
|
||||
for (var i = 0; i < renderTargets.Length; i++)
|
||||
{
|
||||
var handle = renderTargets[i];
|
||||
if (!handle.IsValid)
|
||||
{
|
||||
RecordError(nameof(SetRenderTargets), Error.InvalidArgument);
|
||||
continue;
|
||||
}
|
||||
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(handle.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(SetRenderTargets), recordResult.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
var viewGroup = recordResult.Value.viewGroup;
|
||||
pRtvHandles[i] = _descriptorAllocator.GetCpuHandle(viewGroup.rtv);
|
||||
|
||||
rtvCount++;
|
||||
}
|
||||
|
||||
var pDsvHandle = stackalloc D3D12_CPU_DESCRIPTOR_HANDLE[depthTarget.IsValid ? 1 : 0];
|
||||
if (pDsvHandle != null)
|
||||
{
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(depthTarget.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(SetRenderTargets), recordResult.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var viewGroup = recordResult.Value.viewGroup;
|
||||
pDsvHandle[0] = _descriptorAllocator.GetCpuHandle(viewGroup.dsv);
|
||||
}
|
||||
|
||||
_commandList.Get()->OMSetRenderTargets(rtvCount, pRtvHandles, FALSE, pDsvHandle);
|
||||
}
|
||||
|
||||
public void ClearRenderTargetView(Handle<Texture> renderTarget, Color128 clearColor)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(renderTarget.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(ClearRenderTargetView), recordResult.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
ref var record = ref recordResult.Value;
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
|
||||
|
||||
_commandList.Get()->ClearRenderTargetView(cpuHandle, (float*)&clearColor, 0, null);
|
||||
}
|
||||
|
||||
public void ClearDepthStencilView(Handle<Texture> depthStencil, bool inlcudeDepth, bool includeStencil, float clearDepth = 1.0f, byte clearStencil = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(depthStencil.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(ClearDepthStencilView), recordResult.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
ref var record = ref recordResult.Value;
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
|
||||
var flag = (inlcudeDepth ? D3D12_CLEAR_FLAG_DEPTH : 0) | (includeStencil ? D3D12_CLEAR_FLAG_STENCIL : 0);
|
||||
|
||||
_commandList.Get()->ClearDepthStencilView(cpuHandle,
|
||||
flag,
|
||||
clearDepth,
|
||||
clearStencil,
|
||||
0,
|
||||
null);
|
||||
}
|
||||
|
||||
public void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var pRtvDescs = stackalloc D3D12_RENDER_PASS_RENDER_TARGET_DESC[rtDescs.Length];
|
||||
for (var i = 0; i < rtDescs.Length; i++)
|
||||
{
|
||||
var rtDesc = rtDescs[i];
|
||||
if (rtDesc.Texture.IsInvalid)
|
||||
{
|
||||
RecordError(nameof(BeginRenderPass), Error.InvalidArgument);
|
||||
continue;
|
||||
}
|
||||
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(rtDesc.Texture.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(BeginRenderPass), recordResult.Error);
|
||||
continue;
|
||||
}
|
||||
|
||||
ref var record = ref recordResult.Value;
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.rtv);
|
||||
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
|
||||
var clearColor = rtDesc.ClearColor;
|
||||
|
||||
// Map load operation
|
||||
var loadAccessType = rtDesc.LoadOp switch
|
||||
{
|
||||
AttachmentLoadOp.Load => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE,
|
||||
AttachmentLoadOp.Clear => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
|
||||
AttachmentLoadOp.DontCare => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD,
|
||||
_ => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE
|
||||
};
|
||||
|
||||
// Map store operation
|
||||
var storeAccessType = rtDesc.StoreOp switch
|
||||
{
|
||||
AttachmentStoreOp.Store => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
|
||||
AttachmentStoreOp.DontCare => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD,
|
||||
_ => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE
|
||||
};
|
||||
|
||||
var desc = new D3D12_RENDER_PASS_RENDER_TARGET_DESC
|
||||
{
|
||||
cpuDescriptor = cpuHandle,
|
||||
BeginningAccess = new D3D12_RENDER_PASS_BEGINNING_ACCESS
|
||||
{
|
||||
Type = loadAccessType,
|
||||
Clear = loadAccessType == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR
|
||||
? new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
|
||||
{
|
||||
ClearValue = new D3D12_CLEAR_VALUE(format, (float*)&clearColor)
|
||||
}
|
||||
: default
|
||||
},
|
||||
EndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS
|
||||
{
|
||||
Type = storeAccessType
|
||||
}
|
||||
};
|
||||
|
||||
pRtvDescs[i] = desc;
|
||||
}
|
||||
|
||||
var pDsvDesc = stackalloc D3D12_RENDER_PASS_DEPTH_STENCIL_DESC[depthDesc.Texture.IsValid ? 1 : 0];
|
||||
if (pDsvDesc != null)
|
||||
{
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(depthDesc.Texture.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(BeginRenderPass), recordResult.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
ref var record = ref recordResult.Value;
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(record.viewGroup.dsv);
|
||||
var format = record.desc.TextureDescription.Format.ToDXGIFormat();
|
||||
|
||||
// Map depth load operation
|
||||
var depthLoadAccessType = depthDesc.DepthLoadOp switch
|
||||
{
|
||||
AttachmentLoadOp.Load => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE,
|
||||
AttachmentLoadOp.Clear => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
|
||||
AttachmentLoadOp.DontCare => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD,
|
||||
_ => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE
|
||||
};
|
||||
|
||||
// Map depth store operation
|
||||
var depthStoreAccessType = depthDesc.DepthStoreOp switch
|
||||
{
|
||||
AttachmentStoreOp.Store => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
|
||||
AttachmentStoreOp.DontCare => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD,
|
||||
_ => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE
|
||||
};
|
||||
|
||||
// Map stencil load operation
|
||||
var stencilLoadAccessType = depthDesc.StencilLoadOp switch
|
||||
{
|
||||
AttachmentLoadOp.Load => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE,
|
||||
AttachmentLoadOp.Clear => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
|
||||
AttachmentLoadOp.DontCare => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD,
|
||||
_ => D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE
|
||||
};
|
||||
|
||||
// Map stencil store operation
|
||||
var stencilStoreAccessType = depthDesc.StencilStoreOp switch
|
||||
{
|
||||
AttachmentStoreOp.Store => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
|
||||
AttachmentStoreOp.DontCare => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD,
|
||||
_ => D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE
|
||||
};
|
||||
|
||||
var desc = new D3D12_RENDER_PASS_DEPTH_STENCIL_DESC
|
||||
{
|
||||
cpuDescriptor = cpuHandle,
|
||||
DepthBeginningAccess = new D3D12_RENDER_PASS_BEGINNING_ACCESS
|
||||
{
|
||||
Type = depthLoadAccessType,
|
||||
Clear = depthLoadAccessType == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR
|
||||
? new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
|
||||
{
|
||||
ClearValue = new D3D12_CLEAR_VALUE(format, depthDesc.ClearDepth, depthDesc.ClearStencil)
|
||||
}
|
||||
: default
|
||||
},
|
||||
DepthEndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS
|
||||
{
|
||||
Type = depthStoreAccessType
|
||||
},
|
||||
StencilBeginningAccess = new D3D12_RENDER_PASS_BEGINNING_ACCESS
|
||||
{
|
||||
Type = stencilLoadAccessType,
|
||||
Clear = stencilLoadAccessType == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR
|
||||
? new D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS
|
||||
{
|
||||
ClearValue = new D3D12_CLEAR_VALUE(format, depthDesc.ClearDepth, depthDesc.ClearStencil)
|
||||
}
|
||||
: default
|
||||
},
|
||||
StencilEndingAccess = new D3D12_RENDER_PASS_ENDING_ACCESS
|
||||
{
|
||||
Type = stencilStoreAccessType
|
||||
}
|
||||
};
|
||||
|
||||
pDsvDesc[0] = desc;
|
||||
}
|
||||
|
||||
_commandList.Get()->BeginRenderPass((uint)rtDescs.Length, pRtvDescs, pDsvDesc,
|
||||
allowUAVWrites ? D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES : D3D12_RENDER_PASS_FLAG_NONE);
|
||||
}
|
||||
|
||||
public void EndRenderPass()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->EndRenderPass();
|
||||
}
|
||||
|
||||
public void SetViewport(ViewportDesc viewport)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var d3d12Viewport = new D3D12_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height, viewport.MinDepth, viewport.MaxDepth);
|
||||
_commandList.Get()->RSSetViewports(1, &d3d12Viewport);
|
||||
}
|
||||
|
||||
public void SetPipelineState(Key128<GraphicsPipeline> pipelineKey)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var psor = _pipelineLibrary.GetGraphicsPSO(pipelineKey);
|
||||
if (psor.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(SetPipelineState), psor.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_commandList.Get()->SetGraphicsRootSignature(_pipelineLibrary.DefaultRootSignature);
|
||||
_commandList.Get()->SetPipelineState(psor.Value);
|
||||
}
|
||||
|
||||
public void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||
_commandList.Get()->SetGraphicsRootConstantBufferView(slot, resource.Get()->GetGPUVirtualAddress());
|
||||
}
|
||||
|
||||
public void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var recordResult = _resourceDatabase.GetResourceRecord(buffer.AsResource());
|
||||
if (recordResult.Error != Error.None)
|
||||
{
|
||||
RecordError(nameof(BeginRenderPass), recordResult.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
ref var record = ref recordResult.Value;
|
||||
var vbView = new D3D12_VERTEX_BUFFER_VIEW
|
||||
{
|
||||
BufferLocation = record.ResourcePtr.Get()->GetGPUVirtualAddress() + offset,
|
||||
SizeInBytes = (uint)(record.ResourcePtr.Get()->GetDesc().Width - offset),
|
||||
StrideInBytes = record.desc.BufferDescription.Stride
|
||||
};
|
||||
|
||||
_commandList.Get()->IASetVertexBuffers(slot, 1, &vbView);
|
||||
}
|
||||
|
||||
public void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var resource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||
var ibView = new D3D12_INDEX_BUFFER_VIEW
|
||||
{
|
||||
BufferLocation = resource.Get()->GetGPUVirtualAddress() + offset,
|
||||
SizeInBytes = (uint)(resource.Get()->GetDesc().Width - offset),
|
||||
Format = type == IndexType.UInt16 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT
|
||||
};
|
||||
|
||||
_commandList.Get()->IASetIndexBuffer(&ibView);
|
||||
}
|
||||
|
||||
public void SetPrimitiveTopology(PrimitiveTopology topology)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var d3d12Topology = topology switch
|
||||
{
|
||||
PrimitiveTopology.Point => D3D_PRIMITIVE_TOPOLOGY_POINTLIST,
|
||||
PrimitiveTopology.Line => D3D_PRIMITIVE_TOPOLOGY_LINELIST,
|
||||
PrimitiveTopology.Triangle => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
|
||||
_ => D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST
|
||||
};
|
||||
|
||||
_commandList.Get()->IASetPrimitiveTopology(d3d12Topology);
|
||||
}
|
||||
|
||||
public void SetGraphicsRoot32Constants(uint rootIndex, ReadOnlySpan<uint> constantBuffer, uint offsetIn32Bits = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
fixed (uint* pConstants = constantBuffer)
|
||||
{
|
||||
_commandList.Get()->SetGraphicsRoot32BitConstants(rootIndex, (uint)constantBuffer.Length, pConstants, offsetIn32Bits);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->DrawInstanced(vertexCount, instanceCount, startVertex, startInstance);
|
||||
}
|
||||
|
||||
public void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->DrawIndexedInstanced(indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
||||
}
|
||||
|
||||
public void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->Dispatch(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
}
|
||||
|
||||
public void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
_commandList.Get()->DispatchMesh(threadGroupCountX, threadGroupCountY, threadGroupCountZ);
|
||||
}
|
||||
|
||||
public void DispatchRay()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
// ThrowIfDisposed();
|
||||
// ThrowIfNotRecording();
|
||||
// IncrementCommandCount();
|
||||
|
||||
// _device.Get()->DispatchRays();
|
||||
}
|
||||
|
||||
public void DispatchGraph()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ExecuteIndirect(Handle<GraphicsBuffer> argumentBuffer, ulong argumentOffset, Handle<GraphicsBuffer> countBuffer, ulong countBufferOffset)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
IncrementCommandCount();
|
||||
var resource = _resourceDatabase.GetResource(argumentBuffer.AsResource());
|
||||
var countResource = _resourceDatabase.GetResource(countBuffer.AsResource());
|
||||
_commandList.Get()->ExecuteIndirect(null, 0,
|
||||
resource, argumentOffset, countResource, countBufferOffset);
|
||||
|
||||
}
|
||||
|
||||
public void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data)
|
||||
where T : unmanaged
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var sizeInBytes = (uint)(data.Length * sizeof(T));
|
||||
|
||||
var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(sizeInBytes, out var offset);
|
||||
var uploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
|
||||
|
||||
void* pMappedData;
|
||||
uploadResource.Get()->Map(0, null, &pMappedData);
|
||||
fixed (T* pData = data)
|
||||
{
|
||||
MemoryUtility.MemCpy((byte*)pMappedData + offset, pData, sizeInBytes);
|
||||
}
|
||||
uploadResource.Get()->Unmap(0, null);
|
||||
|
||||
var pResource = _resourceDatabase.GetResource(buffer.AsResource());
|
||||
_commandList.Get()->CopyBufferRegion(pResource, 0, uploadResource, offset, sizeInBytes);
|
||||
}
|
||||
|
||||
public void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var resource = _resourceDatabase.GetResource(texture.AsResource());
|
||||
|
||||
var resourceDesc = resource.Get()->GetDesc();
|
||||
var requiredSize = GetRequiredIntermediateSize(resource, 0, (uint)subresources.Length);
|
||||
|
||||
var uploadHandle = _resourceAllocator.CreateTempUploadBuffer(requiredSize, out var offset);
|
||||
var pUploadResource = _resourceDatabase.GetResource(uploadHandle.AsResource());
|
||||
|
||||
var d3d12Subresources = stackalloc D3D12_SUBRESOURCE_DATA[subresources.Length];
|
||||
for (var i = 0; i < subresources.Length; i++)
|
||||
{
|
||||
d3d12Subresources[i] = new D3D12_SUBRESOURCE_DATA
|
||||
{
|
||||
pData = subresources[i].pData,
|
||||
RowPitch = (nint)subresources[i].rowPitch,
|
||||
SlicePitch = (nint)subresources[i].slicePitch
|
||||
};
|
||||
}
|
||||
|
||||
UpdateSubresources(
|
||||
(ID3D12GraphicsCommandList*)_commandList.Get(),
|
||||
resource,
|
||||
pUploadResource,
|
||||
offset,
|
||||
0,
|
||||
(uint)subresources.Length,
|
||||
d3d12Subresources);
|
||||
}
|
||||
|
||||
public void CopyBuffer(Handle<GraphicsBuffer> dest, Handle<GraphicsBuffer> src, ulong destOffset = 0, ulong srcOffset = 0, ulong numBytes = 0)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ThrowIfNotRecording();
|
||||
#if !DEBUG
|
||||
if (_lastError.Status != Error.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
IncrementCommandCount();
|
||||
|
||||
var pDestResource = _resourceDatabase.GetResource(dest.AsResource());
|
||||
var pSrcResource = _resourceDatabase.GetResource(src.AsResource());
|
||||
if (pSrcResource == null || pDestResource == null)
|
||||
{
|
||||
RecordError(nameof(CopyBuffer), Error.InvalidArgument);
|
||||
return;
|
||||
}
|
||||
|
||||
if (numBytes == 0)
|
||||
{
|
||||
_commandList.Get()->CopyResource(pDestResource, pSrcResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
_commandList.Get()->CopyBufferRegion(pDestResource, destOffset, pSrcResource, srcOffset, numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isRecording)
|
||||
{
|
||||
throw new InvalidOperationException("Command buffer is still recording");
|
||||
}
|
||||
|
||||
_commandList.Dispose();
|
||||
_commandCount = 0;
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of command queue interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12CommandQueue : ICommandQueue
|
||||
{
|
||||
private UniquePtr<ID3D12CommandQueue> _commandQueue;
|
||||
private UniquePtr<ID3D12Fence1> _fence;
|
||||
|
||||
private readonly AutoResetEvent _fenceEvent;
|
||||
private ulong _fenceValue;
|
||||
private bool _disposed;
|
||||
|
||||
public CommandQueueType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SharedPtr<ID3D12CommandQueue> NativeQueue => _commandQueue.Get();
|
||||
|
||||
public D3D12CommandQueue(ID3D12Device14* pDevice, CommandQueueType type)
|
||||
{
|
||||
Type = type;
|
||||
_fenceEvent = new AutoResetEvent(false);
|
||||
_fenceValue = 0;
|
||||
|
||||
var queueDesc = new D3D12_COMMAND_QUEUE_DESC
|
||||
{
|
||||
Type = ConvertCommandQueueType(type),
|
||||
Priority = (int)D3D12_COMMAND_QUEUE_PRIORITY.D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
|
||||
Flags = D3D12_COMMAND_QUEUE_FLAGS.D3D12_COMMAND_QUEUE_FLAG_NONE,
|
||||
};
|
||||
|
||||
ID3D12CommandQueue* pQueue = default;
|
||||
ID3D12Fence1* pFence = default;
|
||||
ThrowIfFailed(pDevice->CreateCommandQueue(&queueDesc, __uuidof(pQueue), (void**)&pQueue));
|
||||
ThrowIfFailed(pDevice->CreateFence(0, D3D12_FENCE_FLAGS.D3D12_FENCE_FLAG_NONE, __uuidof(pFence), (void**)&pFence));
|
||||
|
||||
_commandQueue.Attach(pQueue);
|
||||
_fence.Attach(pFence);
|
||||
}
|
||||
|
||||
~D3D12CommandQueue()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private static D3D12_COMMAND_LIST_TYPE ConvertCommandQueueType(CommandQueueType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
CommandQueueType.Graphics => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
CommandQueueType.Compute => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
||||
CommandQueueType.Copy => D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_COPY,
|
||||
_ => throw new ArgumentException($"Unknown command queue type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
public void Submit(ICommandBuffer commandBuffer)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (commandBuffer.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandBuffer is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
var commandList = d3d12CommandBuffer.NativeCommandList;
|
||||
var commandListPtr = (ID3D12CommandList*)commandList.Get();
|
||||
_commandQueue.Get()->ExecuteCommandLists(1, &commandListPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Command buffer must be a D3D12CommandBuffer", nameof(commandBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
Span<int> executableIndices = stackalloc int[commandBuffers.Length];
|
||||
executableIndices.Fill(-1);
|
||||
|
||||
var currentIndex = 0;
|
||||
for (var i = 0; i < commandBuffers.Length; i++)
|
||||
{
|
||||
if (!commandBuffers[i].IsEmpty)
|
||||
{
|
||||
executableIndices[currentIndex] = i;
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
var ppCommandLists = stackalloc ID3D12CommandList*[commandBuffers.Length];
|
||||
|
||||
currentIndex = 0;
|
||||
while (currentIndex < commandBuffers.Length)
|
||||
{
|
||||
var cmdIndex = executableIndices[currentIndex];
|
||||
if (cmdIndex == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (commandBuffers[cmdIndex] is D3D12CommandBuffer d3d12CommandBuffer)
|
||||
{
|
||||
ppCommandLists[currentIndex] = (ID3D12CommandList*)d3d12CommandBuffer.NativeCommandList.Get();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Command buffer must be a D3D12CommandBuffer", nameof(commandBuffers));
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
_commandQueue.Get()->ExecuteCommandLists((uint)currentIndex, ppCommandLists);
|
||||
}
|
||||
|
||||
public ulong Signal(ulong value)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
_fenceValue = value;
|
||||
ThrowIfFailed(_commandQueue.Get()->Signal((ID3D12Fence*)_fence.Get(), _fenceValue));
|
||||
return _fenceValue;
|
||||
}
|
||||
|
||||
public void WaitForValue(ulong value)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_fence.Get()->GetCompletedValue() < value)
|
||||
{
|
||||
var handle = new HANDLE((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||
if (_fence.Get()->SetEventOnCompletion(value, handle).SUCCEEDED)
|
||||
{
|
||||
_fenceEvent.WaitOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetCompletedValue()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _fence.Get()->GetCompletedValue();
|
||||
}
|
||||
|
||||
public void WaitIdle()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var fenceValue = Signal(Interlocked.Increment(ref _fenceValue));
|
||||
WaitForValue(fenceValue);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_commandQueue.Dispose();
|
||||
_fence.Dispose();
|
||||
_fenceEvent?.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12DebugLayer
|
||||
{
|
||||
private UniquePtr<ID3D12Debug6> _d3d12Debug;
|
||||
private UniquePtr<IDXGIDebug1> _dxgiDebug;
|
||||
private UniquePtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||
|
||||
public D3D12DebugLayer()
|
||||
{
|
||||
ID3D12Debug6* pDebug = default;
|
||||
ThrowIfFailed(D3D12GetDebugInterface(__uuidof(pDebug), (void**)&pDebug));
|
||||
pDebug->EnableDebugLayer();
|
||||
|
||||
IDXGIDebug1* pDxgiDebug = default;
|
||||
ThrowIfFailed(DXGIGetDebugInterface1(0u, __uuidof(pDxgiDebug), (void**)&pDxgiDebug));
|
||||
pDxgiDebug->EnableLeakTrackingForThread();
|
||||
|
||||
IDXGIInfoQueue* pDxgiInfoQueue = default;
|
||||
ThrowIfFailed(DXGIGetDebugInterface1(0u, __uuidof(pDxgiInfoQueue), (void**)&pDxgiInfoQueue));
|
||||
ThrowIfFailed(pDxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true));
|
||||
ThrowIfFailed(pDxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true));
|
||||
|
||||
_d3d12Debug.Attach(pDebug);
|
||||
_dxgiDebug.Attach(pDxgiDebug);
|
||||
_dxgiInfoQueue.Attach(pDxgiInfoQueue);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ThrowIfFailed(_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL | DXGI_DEBUG_RLO_IGNORE_INTERNAL));
|
||||
|
||||
_d3d12Debug.Dispose();
|
||||
_dxgiDebug.Dispose();
|
||||
_dxgiInfoQueue.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of viewGroup allocator that manages different types of viewGroup heaps.
|
||||
/// </summary>
|
||||
internal unsafe class D3D12DescriptorAllocator : IDisposable
|
||||
{
|
||||
private readonly D3D12DescriptorHeap _rtvHeap;
|
||||
private readonly D3D12DescriptorHeap _dsvHeap;
|
||||
private readonly D3D12DescriptorHeap _cbvSrvUavHeap;
|
||||
private readonly D3D12DescriptorHeap _samplerHeap;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12DescriptorAllocator(D3D12RenderDevice device, int initialRtvCount = 512, int initialDsvCount = 512, int initialSrvCount = 200_000, int initialSamplerCount = 256)
|
||||
{
|
||||
_rtvHeap = new D3D12DescriptorHeap("rtv", device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, initialRtvCount);
|
||||
_dsvHeap = new D3D12DescriptorHeap("dsv", device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, initialDsvCount);
|
||||
_cbvSrvUavHeap = new D3D12DescriptorHeap("srv", device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, initialSrvCount);
|
||||
_samplerHeap = new D3D12DescriptorHeap("sampler", device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, initialSamplerCount);
|
||||
}
|
||||
|
||||
~D3D12DescriptorAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
#region RTV Methods
|
||||
|
||||
public Identifier<RTVDescriptor> AllocateRTV()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _rtvHeap.AllocateDescriptor();
|
||||
if (index == -1)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate RTV descriptor");
|
||||
}
|
||||
|
||||
return new Identifier<RTVDescriptor>(index);
|
||||
}
|
||||
|
||||
public Identifier<RTVDescriptor>[] AllocateRTVs(int count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _rtvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == -1)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} RTV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new Identifier<RTVDescriptor>[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
descriptors[i] = new Identifier<RTVDescriptor>(index);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier<RTVDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _rtvHeap.GetCpuHandle(descriptor.Value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Release(Identifier<RTVDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_rtvHeap.ReleaseDescriptor(descriptor.Value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Release(ReadOnlySpan<Identifier<RTVDescriptor>> descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
Release(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DSV Methods
|
||||
|
||||
public Identifier<DSVDescriptor> AllocateDSV()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _dsvHeap.AllocateDescriptor();
|
||||
if (index == -1)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate DSV descriptor");
|
||||
}
|
||||
|
||||
return new Identifier<DSVDescriptor>(index);
|
||||
}
|
||||
|
||||
public Identifier<DSVDescriptor>[] AllocateDSVs(int count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _dsvHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == -1)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} DSV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new Identifier<DSVDescriptor>[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
descriptors[i] = new Identifier<DSVDescriptor>(index);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier<DSVDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _dsvHeap.GetCpuHandle(descriptor.Value);
|
||||
}
|
||||
|
||||
public void Release(Identifier<DSVDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_dsvHeap.ReleaseDescriptor(descriptor.Value);
|
||||
}
|
||||
|
||||
public void Release(ReadOnlySpan<Identifier<DSVDescriptor>> descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
Release(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CBV_SRV_UAV Methods
|
||||
|
||||
public Identifier<CbvSrvUavDescriptor> AllocateCbvSrvUav()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _cbvSrvUavHeap.AllocateDescriptor();
|
||||
if (index == -1)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate CBV/SRV/UAV descriptor");
|
||||
}
|
||||
|
||||
return new Identifier<CbvSrvUavDescriptor>(index);
|
||||
}
|
||||
|
||||
public Identifier<CbvSrvUavDescriptor>[] AllocateSRVs(int count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _cbvSrvUavHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == -1)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} CBV/SRV/UAV descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new Identifier<CbvSrvUavDescriptor>[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
descriptors[i] = new Identifier<CbvSrvUavDescriptor>(index);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void CopyToShaderVisible(Identifier<CbvSrvUavDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_cbvSrvUavHeap.CopyToShaderVisibleHeap(descriptor.Value);
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier<CbvSrvUavDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _cbvSrvUavHeap.GetCpuHandle(descriptor.Value);
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(Identifier<CbvSrvUavDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _cbvSrvUavHeap.GetCpuHandleShaderVisible(descriptor.Value);
|
||||
}
|
||||
|
||||
public D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(Identifier<CbvSrvUavDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _cbvSrvUavHeap.GetGpuHandle(descriptor.Value);
|
||||
}
|
||||
|
||||
public void Release(Identifier<CbvSrvUavDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_cbvSrvUavHeap.ReleaseDescriptor(descriptor.Value);
|
||||
}
|
||||
|
||||
public void Release(ReadOnlySpan<Identifier<CbvSrvUavDescriptor>> descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
Release(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sampler Methods
|
||||
|
||||
public Identifier<SamplerDescriptor> AllocateSampler()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var index = _samplerHeap.AllocateDescriptor();
|
||||
if (index == -1)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to allocate Sampler descriptor");
|
||||
}
|
||||
|
||||
return new Identifier<SamplerDescriptor>(index);
|
||||
}
|
||||
|
||||
public Identifier<SamplerDescriptor>[] AllocateSamplers(int count)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var baseIndex = _samplerHeap.AllocateDescriptors(count);
|
||||
if (baseIndex == -1)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to allocate {count} Sampler descriptors");
|
||||
}
|
||||
|
||||
var descriptors = new Identifier<SamplerDescriptor>[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var index = baseIndex + i;
|
||||
descriptors[i] = new Identifier<SamplerDescriptor>(index);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public void CopyToShaderVisible(Identifier<SamplerDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_samplerHeap.CopyToShaderVisibleHeap(descriptor.Value);
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(Identifier<SamplerDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _samplerHeap.GetCpuHandle(descriptor.Value);
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(Identifier<SamplerDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _samplerHeap.GetCpuHandleShaderVisible(descriptor.Value);
|
||||
}
|
||||
|
||||
public D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(Identifier<SamplerDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _samplerHeap.GetGpuHandle(descriptor.Value);
|
||||
}
|
||||
|
||||
public void Release(Identifier<SamplerDescriptor> descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
_samplerHeap.ReleaseDescriptor(descriptor.Value);
|
||||
}
|
||||
|
||||
public void Release(ReadOnlySpan<Identifier<SamplerDescriptor>> descriptors)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var descriptor in descriptors)
|
||||
{
|
||||
Release(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Release(ResourceViewGroup descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
Release(descriptor.rtv);
|
||||
Release(descriptor.dsv);
|
||||
Release(descriptor.srv);
|
||||
Release(descriptor.cbv);
|
||||
Release(descriptor.uav);
|
||||
Release(descriptor.sampler);
|
||||
}
|
||||
|
||||
#region Utility Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RTV Heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetRTVHeap() => _rtvHeap.Heap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DSV Heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetDSVHeap() => _dsvHeap.Heap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CBV/SRV/UAV Heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetCbvSrvUavHeap() => _cbvSrvUavHeap.ShaderVisibleHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampler Heap for binding to the command list.
|
||||
/// </summary>
|
||||
public ID3D12DescriptorHeap* GetSamplerHeap() => _samplerHeap.ShaderVisibleHeap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the shader visible heaps that need to be bound to the command list.
|
||||
/// </summary>
|
||||
/// <param name="ppHeap">An array of two ID3D12DescriptorHeap pointers to receive the CBV/SRV/UAV and Sampler heaps.</param>
|
||||
public void GetShaderVisibleHeaps(ID3D12DescriptorHeap** ppHeap)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (ppHeap == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ppHeap));
|
||||
}
|
||||
|
||||
ppHeap[0] = _cbvSrvUavHeap.ShaderVisibleHeap;
|
||||
ppHeap[1] = _samplerHeap.ShaderVisibleHeap;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_rtvHeap.Dispose();
|
||||
_dsvHeap.Dispose();
|
||||
_cbvSrvUavHeap.Dispose();
|
||||
_samplerHeap.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal unsafe class D3D12DescriptorHeap : IDisposable
|
||||
{
|
||||
private const int _INVALID_DESCRIPTOR_INDEX = -1;
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
|
||||
private UniquePtr<ID3D12DescriptorHeap> _heap;
|
||||
private UniquePtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandle;
|
||||
private D3D12_CPU_DESCRIPTOR_HANDLE _startCpuHandleShaderVisible;
|
||||
private D3D12_GPU_DESCRIPTOR_HANDLE _startGpuHandleShaderVisible;
|
||||
private int _searchStart;
|
||||
private UnsafeBitSet _allocatedDescriptors;
|
||||
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public D3D12_DESCRIPTOR_HEAP_TYPE HeapType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public int NumDescriptors
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public int NumAllocatedDescriptors
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public bool ShaderVisible
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public uint Stride
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ID3D12DescriptorHeap* Heap => _heap.Get();
|
||||
public ID3D12DescriptorHeap* ShaderVisibleHeap => _shaderVisibleHeap.Get();
|
||||
|
||||
public D3D12DescriptorHeap(string name, D3D12RenderDevice device, D3D12_DESCRIPTOR_HEAP_TYPE type, int numDescriptors)
|
||||
{
|
||||
numDescriptors = Math.Max(64, numDescriptors);
|
||||
|
||||
_device = device;
|
||||
|
||||
HeapType = type;
|
||||
NumDescriptors = numDescriptors;
|
||||
ShaderVisible = type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || type == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
|
||||
Stride = device.NativeDevice.Get()->GetDescriptorHandleIncrementSize(type);
|
||||
|
||||
var success = AllocateResources(numDescriptors);
|
||||
Debug.Assert(success);
|
||||
|
||||
_heap.Get()->SetName(name);
|
||||
if (ShaderVisible)
|
||||
{
|
||||
_shaderVisibleHeap.Get()->SetName($"{name} Shader Visible");
|
||||
}
|
||||
}
|
||||
|
||||
public int AllocateDescriptor() => AllocateDescriptors(1);
|
||||
|
||||
public int AllocateDescriptors(int count)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var foundIndex = 0;
|
||||
uint freeCount = 0;
|
||||
var found = false;
|
||||
|
||||
// Find a contiguous range of 'count' indices for which _allocatedDescriptors[index] is false
|
||||
for (var index = _searchStart; index < NumDescriptors; index++)
|
||||
{
|
||||
if (_allocatedDescriptors.IsSet(index))
|
||||
{
|
||||
freeCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
freeCount += 1;
|
||||
}
|
||||
|
||||
if (freeCount >= count)
|
||||
{
|
||||
foundIndex = index > 0 ? index - count + 1 : 0;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
foundIndex = NumDescriptors;
|
||||
|
||||
if (!Grow(NumDescriptors + count))
|
||||
{
|
||||
Debug.WriteLine("Error: Failed to grow descriptor heap.");
|
||||
return _INVALID_DESCRIPTOR_INDEX;
|
||||
}
|
||||
}
|
||||
|
||||
for (var index = foundIndex; index < foundIndex + count; index++)
|
||||
{
|
||||
_allocatedDescriptors.SetBit(index);
|
||||
}
|
||||
|
||||
NumAllocatedDescriptors += count;
|
||||
_searchStart = foundIndex + count;
|
||||
return foundIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReleaseDescriptor(int index) => ReleaseDescriptors(index, 1);
|
||||
|
||||
public void ReleaseDescriptors(int baseIndex, int count = 1)
|
||||
{
|
||||
if (baseIndex == _INVALID_DESCRIPTOR_INDEX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
for (var index = baseIndex; index < baseIndex + count; index++)
|
||||
{
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (!_allocatedDescriptors.IsSet(index))
|
||||
{
|
||||
Debug.WriteLine("Error: Attempted to release an un-allocated descriptor");
|
||||
}
|
||||
#endif
|
||||
|
||||
_allocatedDescriptors.ClearBit(index);
|
||||
}
|
||||
|
||||
NumAllocatedDescriptors -= count;
|
||||
|
||||
if (_searchStart > baseIndex)
|
||||
{
|
||||
_searchStart = baseIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandle(int index)
|
||||
{
|
||||
if (index < 0 || index >= NumDescriptors)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Descriptor index is out of range.");
|
||||
}
|
||||
|
||||
var handle = _startCpuHandle;
|
||||
return handle.Offset(index, Stride);
|
||||
}
|
||||
|
||||
public D3D12_CPU_DESCRIPTOR_HANDLE GetCpuHandleShaderVisible(int index)
|
||||
{
|
||||
if (index < 0 || index >= NumDescriptors)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Descriptor index is out of range.");
|
||||
}
|
||||
|
||||
if (!ShaderVisible)
|
||||
{
|
||||
throw new InvalidOperationException("Descriptor heap is not shader visible.");
|
||||
}
|
||||
|
||||
var handle = _startCpuHandleShaderVisible;
|
||||
return handle.Offset(index, Stride);
|
||||
}
|
||||
|
||||
public D3D12_GPU_DESCRIPTOR_HANDLE GetGpuHandle(int index)
|
||||
{
|
||||
if (index < 0 || index >= NumDescriptors)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Descriptor index is out of range.");
|
||||
}
|
||||
|
||||
if (!ShaderVisible)
|
||||
{
|
||||
throw new InvalidOperationException("Descriptor heap is not shader visible.");
|
||||
}
|
||||
|
||||
var handle = _startGpuHandleShaderVisible;
|
||||
return handle.Offset(index, Stride);
|
||||
}
|
||||
|
||||
public void CopyToShaderVisibleHeap(int index, int count = 1)
|
||||
{
|
||||
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||
}
|
||||
|
||||
private bool AllocateResources(int numDescriptors)
|
||||
{
|
||||
NumDescriptors = numDescriptors;
|
||||
_heap.Dispose();
|
||||
_shaderVisibleHeap.Dispose();
|
||||
|
||||
D3D12_DESCRIPTOR_HEAP_DESC heapDesc = new()
|
||||
{
|
||||
Type = HeapType,
|
||||
NumDescriptors = (uint)numDescriptors,
|
||||
Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
|
||||
NodeMask = 0
|
||||
};
|
||||
|
||||
ID3D12DescriptorHeap* pHeap = default;
|
||||
var hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pHeap), (void**)&pHeap);
|
||||
if (hr.FAILED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_heap.Attach(pHeap);
|
||||
|
||||
_startCpuHandle = _heap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
|
||||
if (!_allocatedDescriptors.IsCreated)
|
||||
{
|
||||
_allocatedDescriptors = new UnsafeBitSet(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption.Clear);
|
||||
}
|
||||
else
|
||||
{
|
||||
_allocatedDescriptors.Resize(numDescriptors, Misaki.HighPerformance.LowLevel.Buffer.AllocationOption.Clear);
|
||||
}
|
||||
|
||||
if (ShaderVisible)
|
||||
{
|
||||
ID3D12DescriptorHeap* pShaderVisibleHeap = default;
|
||||
|
||||
heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
|
||||
hr = _device.NativeDevice.Get()->CreateDescriptorHeap(&heapDesc, __uuidof(pShaderVisibleHeap), (void**)&pShaderVisibleHeap);
|
||||
if (hr.FAILED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_startCpuHandleShaderVisible = pShaderVisibleHeap->GetCPUDescriptorHandleForHeapStart();
|
||||
_startGpuHandleShaderVisible = pShaderVisibleHeap->GetGPUDescriptorHandleForHeapStart();
|
||||
|
||||
_shaderVisibleHeap.Attach(pShaderVisibleHeap);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool Grow(int minRequiredSize)
|
||||
{
|
||||
var oldSize = NumDescriptors;
|
||||
var newSize = (int)BitOperations.RoundUpToPowerOf2((uint)minRequiredSize);
|
||||
|
||||
var oldHeap = _heap.Detach();
|
||||
|
||||
try
|
||||
{
|
||||
if (!AllocateResources(newSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandle, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
|
||||
if (_shaderVisibleHeap.Get() != null)
|
||||
{
|
||||
_device.NativeDevice.Get()->CopyDescriptorsSimple((uint)oldSize, _startCpuHandleShaderVisible, oldHeap->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
oldHeap->Release();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Debug.Assert(NumAllocatedDescriptors == 0);
|
||||
|
||||
_heap.Dispose();
|
||||
_shaderVisibleHeap.Dispose();
|
||||
_allocatedDescriptors.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
#if DEBUG
|
||||
#define ENABLE_DEBUG
|
||||
#endif
|
||||
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Core;
|
||||
using System.Collections.Immutable;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal class D3D12GraphicsEngine : IGraphicsEngine
|
||||
{
|
||||
private readonly IRenderSystem _renderSystem;
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
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;
|
||||
private readonly D3D12ResourceAllocator _resourceAllocator;
|
||||
|
||||
private ImmutableArray<IRenderer> _renderers;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public IRenderDevice Device => _device;
|
||||
public IShaderCompiler ShaderCompiler => _shaderCompiler;
|
||||
public IPipelineLibrary PipelineLibrary => _pipelineLibrary;
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||
|
||||
public D3D12GraphicsEngine(IRenderSystem renderSystem)
|
||||
{
|
||||
_renderSystem = renderSystem;
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
_debugLayer = new D3D12DebugLayer();
|
||||
#endif
|
||||
_device = new D3D12RenderDevice();
|
||||
_shaderCompiler = new DxcShaderCompiler();
|
||||
_descriptorAllocator = new D3D12DescriptorAllocator(_device);
|
||||
|
||||
_resourceDatabase = new D3D12ResourceDatabase(renderSystem, _descriptorAllocator);
|
||||
_pipelineLibrary = new D3D12PipelineLibrary(_device, _resourceDatabase);
|
||||
_resourceAllocator = new D3D12ResourceAllocator(_device, _descriptorAllocator, _resourceDatabase, _pipelineLibrary);
|
||||
|
||||
_renderers = ImmutableArray<IRenderer>.Empty;
|
||||
|
||||
_pipelineLibrary.InitializeLibrary(null);
|
||||
}
|
||||
|
||||
~D3D12GraphicsEngine()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
}
|
||||
|
||||
public IRenderer CreateRenderer()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var renderer = new D3D12Renderer(this, _resourceDatabase);
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Add(renderer));
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public void RemoveRenderer(IRenderer renderer)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Remove(renderer));
|
||||
}
|
||||
|
||||
public void ClearRenderers()
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
ImmutableInterlocked.Update(ref _renderers, renderers => renderers.Clear());
|
||||
}
|
||||
|
||||
public ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
return new D3D12CommandAllocator(_device, type);
|
||||
}
|
||||
|
||||
public ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
return new D3D12CommandBuffer(
|
||||
_device,
|
||||
_pipelineLibrary,
|
||||
_resourceDatabase,
|
||||
_resourceAllocator,
|
||||
_descriptorAllocator,
|
||||
type);
|
||||
}
|
||||
|
||||
public ISwapChain CreateSwapChain(SwapChainDesc desc)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
return new D3D12SwapChain(_resourceDatabase, _descriptorAllocator, _device, desc, _renderSystem.MaxFrameLatency);
|
||||
}
|
||||
|
||||
public Result RenderFrame(ICommandAllocator commandAllocator)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
|
||||
var r = Result.Success();
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
r = renderer.Render(commandAllocator);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_resourceDatabase.EndFrame();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var renderer in _renderers)
|
||||
{
|
||||
renderer.Dispose();
|
||||
}
|
||||
|
||||
_resourceDatabase.ReleaseAllResourcesImmediately();
|
||||
|
||||
_resourceAllocator.Dispose();
|
||||
_pipelineLibrary.Dispose();
|
||||
_resourceDatabase.Dispose();
|
||||
|
||||
_descriptorAllocator.Dispose();
|
||||
_shaderCompiler.Dispose();
|
||||
_device.Dispose();
|
||||
#if ENABLE_DEBUG
|
||||
_debugLayer.Dispose();
|
||||
#endif
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Aliases.D3D_Alias;
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal struct D3D12PipelineState : IDisposable
|
||||
{
|
||||
public D3DX12_MESH_SHADER_PIPELINE_STATE_DESC psoDesc;
|
||||
public UniquePtr<ID3D12PipelineState> pso;
|
||||
public Key64<ShaderVariant> shaderVariant;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
pso.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe class D3D12PipelineLibrary : IPipelineLibrary
|
||||
{
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private UniquePtr<ID3D12PipelineLibrary1> _library;
|
||||
private UniquePtr<ID3D12RootSignature> _defaultRootSignature;
|
||||
|
||||
private readonly Dictionary<Key128<GraphicsPipeline>, D3D12PipelineState> _pipelineCache;
|
||||
|
||||
public ID3D12RootSignature* DefaultRootSignature => _defaultRootSignature.Get();
|
||||
|
||||
public D3D12PipelineLibrary(D3D12RenderDevice device, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
_device = device;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_pipelineCache = new Dictionary<Key128<GraphicsPipeline>, D3D12PipelineState>();
|
||||
|
||||
CreateDefaultRootSignature().ThrowIfFailed();
|
||||
}
|
||||
|
||||
// TODO: Maybe we don't need 4 root signature. We can use bindless for global, per-view, and per-object buffers as well.
|
||||
private Result CreateDefaultRootSignature()
|
||||
{
|
||||
_defaultRootSignature = default;
|
||||
|
||||
// NOTE: Since we are targeting SM 6.6, we can use ResourceDescriptorHeap and SamplerDescriptorHeap directly without needing to set up viewGroup tables.
|
||||
var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[RootSignatureLayout.ROOT_PARAMETER_COUNT];
|
||||
|
||||
rootParameters[0] = new D3D12_ROOT_PARAMETER1
|
||||
{
|
||||
ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
|
||||
ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL,
|
||||
Constants = new D3D12_ROOT_CONSTANTS
|
||||
{
|
||||
ShaderRegister = 0, // b0
|
||||
RegisterSpace = 0, // space0
|
||||
Num32BitValues = 4 // Global, View, Object, Material indices
|
||||
}
|
||||
};
|
||||
|
||||
var rootSignatureDesc = new D3D12_ROOT_SIGNATURE_DESC1
|
||||
{
|
||||
NumParameters = RootSignatureLayout.ROOT_PARAMETER_COUNT,
|
||||
pParameters = rootParameters,
|
||||
NumStaticSamplers = 0,
|
||||
pStaticSamplers = null,
|
||||
Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
|
||||
| D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED
|
||||
| D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED
|
||||
};
|
||||
|
||||
var versionedDesc = new D3D12_VERSIONED_ROOT_SIGNATURE_DESC
|
||||
{
|
||||
Version = D3D_ROOT_SIGNATURE_VERSION_1_1,
|
||||
Desc_1_1 = rootSignatureDesc
|
||||
};
|
||||
|
||||
using ComPtr<ID3DBlob> pSignature = default;
|
||||
using ComPtr<ID3DBlob> pError = default;
|
||||
|
||||
var serializeResult = D3D12SerializeVersionedRootSignature(&versionedDesc, pSignature.GetAddressOf(), pError.GetAddressOf());
|
||||
if (serializeResult.FAILED)
|
||||
{
|
||||
var errorMsg = pError.Get() != null ? Marshal.PtrToStringUTF8((nint)pError.Get()->GetBufferPointer()) : "Unknown error";
|
||||
return Result.Failure($"Failed to serialize default root signature: {errorMsg}");
|
||||
}
|
||||
|
||||
ID3D12RootSignature* pRootSignature = default;
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreateRootSignature(0, pSignature.Get()->GetBufferPointer(), pSignature.Get()->GetBufferSize(),
|
||||
__uuidof(pRootSignature), (void**)&pRootSignature));
|
||||
|
||||
_defaultRootSignature.Attach(pRootSignature);
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void InitializeLibrary(string? filePath)
|
||||
{
|
||||
ID3D12PipelineLibrary1* pLibrary = default;
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var fileBytes = File.ReadAllBytes(filePath);
|
||||
fixed (byte* pFileBytes = fileBytes)
|
||||
{
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(pFileBytes, (nuint)fileBytes.Length, __uuidof(pLibrary), (void**)&pLibrary));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineLibrary(null, 0, __uuidof(pLibrary), (void**)&pLibrary));
|
||||
}
|
||||
|
||||
_library.Attach(pLibrary);
|
||||
}
|
||||
|
||||
public void SaveLibraryToDisk(string filePath)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
throw new InvalidOperationException($"Directory does not exist: {dir}");
|
||||
}
|
||||
|
||||
var size = _library.Get()->GetSerializedSize();
|
||||
using var buffer = new UnsafeArray<byte>((int)size, Allocator.Persistent); // We use persistent Heap allocation instead of stack allocation to avoid stack overflow for large pipeline libraries.
|
||||
|
||||
ThrowIfFailed(_library.Get()->Serialize(buffer.GetUnsafePtr(), size));
|
||||
|
||||
using var fs = File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
fs.Write(buffer.AsSpan());
|
||||
}
|
||||
|
||||
private static Result<CBufferInfo> 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(default(CBufferInfo));
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
var cbufferInfo = new CBufferInfo
|
||||
{
|
||||
Name = rootConstant.Name,
|
||||
RegisterSlot = rootConstant.BindPoint,
|
||||
RegisterSpace = rootConstant.Space,
|
||||
SizeInBytes = rootConstant.Size,
|
||||
Properties = rootConstant.Properties
|
||||
};
|
||||
|
||||
return Result.Success(cbufferInfo);
|
||||
}
|
||||
|
||||
private static D3D12_DEPTH_STENCIL_DESC BuildDepthStencil(ZTest ztest, ZWrite zwrite)
|
||||
{
|
||||
var depthEnabled = ztest != ZTest.Disabled;
|
||||
var writeEnabled = zwrite == ZWrite.On;
|
||||
var cmp = ztest.ToD3DCompare();
|
||||
return D3D12Utility.D3D12_DEPTH_STENCIL_DESC_CREATE(depthEnabled, writeEnabled, cmp);
|
||||
}
|
||||
|
||||
public Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled)
|
||||
{
|
||||
static Result<CBufferInfo> 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 (msr.Value.Properties != null
|
||||
&& msr.Value.SizeInBytes != psr.Value.SizeInBytes)
|
||||
{
|
||||
return Result.Failure("Mesh shader and pixel shader constant buffer layouts do not match.");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (tsr.Value.Properties != null
|
||||
&& tsr.Value.SizeInBytes != psr.Value.SizeInBytes)
|
||||
{
|
||||
return Result.Failure("Task shader and pixel shader constant buffer layouts do not match.");
|
||||
}
|
||||
}
|
||||
|
||||
// ts and ms may not use per material cbuffer at all, so we return the psr value.
|
||||
return psr.Value;
|
||||
}
|
||||
|
||||
if (descriptor.RtvFormats.Length > D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT)
|
||||
{
|
||||
return Result.Failure($"RTV format count exceeds the maximum supported render target count of {D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT}.");
|
||||
}
|
||||
|
||||
var passPipelineKey = new PassPipelineHash(descriptor.RtvFormats, descriptor.DsvFormat);
|
||||
var pipelineKey = RHIUtility.CreateGraphicsPipelineKey(descriptor.VariantKey, descriptor.PipelineOption, passPipelineKey);
|
||||
|
||||
if (!_pipelineCache.ContainsKey(pipelineKey))
|
||||
{
|
||||
//var result = ValidatePassReflectionData(in compiled);
|
||||
//if (result.IsFailure)
|
||||
//{
|
||||
// return Result.Failure(result.Message);
|
||||
//}
|
||||
|
||||
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
|
||||
{
|
||||
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 (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);
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
ID3D12PipelineState* pPipelineState = default;
|
||||
|
||||
var pKeyStr = stackalloc char[33]; // 32 for 128 bits key + 1 for null terminator
|
||||
var keySpan = new Span<char>(pKeyStr, 33);
|
||||
if (!pipelineKey.TryGetString(keySpan))
|
||||
{
|
||||
return Result.Failure("Failed to convert pipeline key to string.");
|
||||
}
|
||||
|
||||
var hr = _library.Get()->LoadPipeline(pKeyStr, &streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState);
|
||||
if (hr == E.E_INVALIDARG)
|
||||
{
|
||||
// Pipeline not found in the library, create a new one.
|
||||
ThrowIfFailed(_device.NativeDevice.Get()->CreatePipelineState(&streamDesc, __uuidof(pPipelineState), (void**)&pPipelineState));
|
||||
ThrowIfFailed(_library.Get()->StorePipeline(pKeyStr, pPipelineState));
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowIfFailed(hr);
|
||||
}
|
||||
|
||||
D3D12PipelineState pso = default;
|
||||
pso.shaderVariant = descriptor.VariantKey;
|
||||
pso.psoDesc = desc;
|
||||
pso.pso.Attach(pPipelineState);
|
||||
|
||||
_pipelineCache[pipelineKey] = pso;
|
||||
}
|
||||
|
||||
return pipelineKey;
|
||||
}
|
||||
|
||||
public bool HasPipeline(Key128<GraphicsPipeline> key)
|
||||
{
|
||||
return _pipelineCache.ContainsKey(key);
|
||||
}
|
||||
|
||||
public Result<SharedPtr<ID3D12PipelineState>, Error> GetGraphicsPSO(Key128<GraphicsPipeline> key)
|
||||
{
|
||||
if (_pipelineCache.TryGetValue(key, out var cacheEntry))
|
||||
{
|
||||
return cacheEntry.pso.Share();
|
||||
}
|
||||
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kvp in _pipelineCache)
|
||||
{
|
||||
kvp.Value.Dispose();
|
||||
}
|
||||
|
||||
_pipelineCache.Clear();
|
||||
|
||||
_defaultRootSignature.Dispose();
|
||||
_library.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static TerraFX.Aliases.D3D_Alias;
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the render device interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12RenderDevice : IRenderDevice
|
||||
{
|
||||
private UniquePtr<ID3D12Device14> _device;
|
||||
private UniquePtr<IDXGIFactory7> _dxgiFactory;
|
||||
private UniquePtr<IDXGIAdapter1> _adapter;
|
||||
|
||||
private readonly D3D12CommandQueue _graphicsQueue;
|
||||
private readonly D3D12CommandQueue _computeQueue;
|
||||
private readonly D3D12CommandQueue _copyQueue;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public ICommandQueue GraphicsQueue => _graphicsQueue;
|
||||
public ICommandQueue ComputeQueue => _computeQueue;
|
||||
public ICommandQueue CopyQueue => _copyQueue;
|
||||
|
||||
public FeatureSupport FeatureSupport
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SharedPtr<IDXGIFactory7> DXGIFactory => _dxgiFactory.Share();
|
||||
public SharedPtr<ID3D12Device14> NativeDevice => _device.Share();
|
||||
public SharedPtr<IDXGIAdapter1> Adapter => _adapter.Share();
|
||||
public SharedPtr<ID3D12CommandQueue> NativeGraphicsQueue => _graphicsQueue.NativeQueue;
|
||||
public SharedPtr<ID3D12CommandQueue> NativeComputeQueue => _computeQueue.NativeQueue;
|
||||
public SharedPtr<ID3D12CommandQueue> NativeCopyQueue => _copyQueue.NativeQueue;
|
||||
|
||||
public D3D12RenderDevice()
|
||||
{
|
||||
IDXGIFactory7* pFactory = default;
|
||||
#if DEBUG
|
||||
ThrowIfFailed(CreateDXGIFactory2(TRUE, __uuidof(pFactory), (void**)&pFactory));
|
||||
#else
|
||||
ThrowIfFailed(CreateDXGIFactory2(FALSE, __uuidof(pFactory), (void**)&pFactory));
|
||||
#endif
|
||||
|
||||
_dxgiFactory.Attach(pFactory);
|
||||
|
||||
InitializeDevice();
|
||||
|
||||
_graphicsQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Graphics);
|
||||
_computeQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Compute);
|
||||
_copyQueue = new D3D12CommandQueue(_device.Get(), CommandQueueType.Copy);
|
||||
|
||||
FeatureSupport = GetFeatureSupport();
|
||||
}
|
||||
|
||||
~D3D12RenderDevice()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void InitializeDevice()
|
||||
{
|
||||
ID3D12Device14* pDevice = default;
|
||||
IDXGIAdapter1* pAdapter = default;
|
||||
|
||||
for (uint adapterIndex = 0;
|
||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, __uuidof(pAdapter), (void**)&pAdapter).SUCCEEDED;
|
||||
adapterIndex++)
|
||||
{
|
||||
DXGI_ADAPTER_DESC1 desc = default;
|
||||
pAdapter->GetDesc1(&desc);
|
||||
|
||||
// Don't select the Basic Render Driver adapter.
|
||||
if (desc.Flags.HasFlag(DXGI_ADAPTER_FLAG_SOFTWARE))
|
||||
{
|
||||
goto NEXT_ITERATION;
|
||||
}
|
||||
|
||||
if (D3D12CreateDevice((IUnknown*)pAdapter, D3D_FEATURE_LEVEL_12_0, __uuidof(pDevice), (void**)&pDevice).SUCCEEDED)
|
||||
{
|
||||
_adapter.Attach(pAdapter);
|
||||
break;
|
||||
}
|
||||
|
||||
NEXT_ITERATION:
|
||||
pAdapter->Release();
|
||||
}
|
||||
|
||||
if (pDevice == null)
|
||||
{
|
||||
pAdapter->Release(); // Dispose the last adapter we tried.
|
||||
throw new PlatformNotSupportedException("Cannot create ID3D12Device with feature level 12.0");
|
||||
}
|
||||
|
||||
_device.Attach(pDevice);
|
||||
}
|
||||
|
||||
private FeatureSupport GetFeatureSupport()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
FeatureSupport support = FeatureSupport.None;
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS options = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS)).SUCCEEDED)
|
||||
{
|
||||
if (options.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3)
|
||||
{
|
||||
support |= FeatureSupport.BindlessResources;
|
||||
}
|
||||
|
||||
if (options.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER.D3D12_RESOURCE_HEAP_TIER_2)
|
||||
{
|
||||
support |= FeatureSupport.AliasBuffersAndTextures;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS5 options5 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options5, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS5)).SUCCEEDED)
|
||||
{
|
||||
if (options5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
support |= FeatureSupport.RayTracing;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS6 options6 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &options6, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS6)).SUCCEEDED)
|
||||
{
|
||||
if (options6.VariableShadingRateTier != D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
support |= FeatureSupport.VariableRateShading;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS7)).SUCCEEDED)
|
||||
{
|
||||
if (options7.MeshShaderTier != D3D12_MESH_SHADER_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
support |= FeatureSupport.MeshShaders;
|
||||
}
|
||||
|
||||
if (options7.SamplerFeedbackTier != D3D12_SAMPLER_FEEDBACK_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
support |= FeatureSupport.SamplerFeedback;
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS21 options9 = default;
|
||||
if (_device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS21, &options9, (uint)sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS8)).SUCCEEDED)
|
||||
{
|
||||
if (options9.WorkGraphsTier != D3D12_WORK_GRAPHS_TIER.D3D12_WORK_GRAPHS_TIER_NOT_SUPPORTED)
|
||||
{
|
||||
support |= FeatureSupport.WorkGraphs;
|
||||
}
|
||||
}
|
||||
|
||||
return support;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_graphicsQueue.Dispose();
|
||||
_computeQueue.Dispose();
|
||||
_copyQueue.Dispose();
|
||||
|
||||
_device.Dispose();
|
||||
_dxgiFactory.Dispose();
|
||||
_adapter.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RenderPasses;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of the renderer interface using RHI abstractions
|
||||
/// </summary>
|
||||
internal class D3D12Renderer : IRenderer
|
||||
{
|
||||
private readonly D3D12GraphicsEngine _graphicsEngine;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
|
||||
private readonly ICommandBuffer _commandBuffer;
|
||||
private readonly RenderGraph _renderGraph;
|
||||
|
||||
private uint _frameIndex;
|
||||
private bool _disposed;
|
||||
|
||||
// NOTE: Testing only.
|
||||
private readonly MeshRenderPass _pass;
|
||||
|
||||
public IRenderOutput? RenderOutput
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
// TODO: Add render graph support
|
||||
|
||||
public D3D12Renderer(D3D12GraphicsEngine graphicsEngine, D3D12ResourceDatabase resourceDatabase)
|
||||
{
|
||||
_graphicsEngine = graphicsEngine;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_commandBuffer = _graphicsEngine.CreateCommandBuffer(CommandBufferType.Graphics);
|
||||
_renderGraph = new RenderGraph(_graphicsEngine);
|
||||
|
||||
// NOTE: Testing only.
|
||||
_pass = new();
|
||||
}
|
||||
|
||||
~D3D12Renderer()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public Result Render(ICommandAllocator commandAllocator)
|
||||
{
|
||||
if (RenderOutput is null)
|
||||
{
|
||||
return Result.Failure("Render target strategy is not set.");
|
||||
}
|
||||
|
||||
var target = RenderOutput.GetRenderTarget();
|
||||
if (target.IsInvalid)
|
||||
{
|
||||
return Result.Failure("Render target is invalid.");
|
||||
}
|
||||
|
||||
_commandBuffer.Begin(commandAllocator);
|
||||
RenderOutput.BeginRender(_commandBuffer);
|
||||
|
||||
// NOTE: Temporary solution: render directly to the swap chain back buffer if available.
|
||||
// HACK: This is hard coded for testing purposes only.
|
||||
|
||||
var error = RenderScene(target, RenderOutput.Viewport, RenderOutput.Scissor);
|
||||
if (error != Error.None)
|
||||
{
|
||||
_commandBuffer.End();
|
||||
return Result.Failure(error);
|
||||
}
|
||||
|
||||
RenderOutput.EndRender(_commandBuffer);
|
||||
var r = _commandBuffer.End();
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r;
|
||||
}
|
||||
|
||||
_graphicsEngine.Device.GraphicsQueue.Submit(_commandBuffer);
|
||||
RenderOutput.Present();
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
// TODO: A proper render graph integration.
|
||||
private Error RenderScene(Handle<Texture> target, ViewportDesc viewport, RectDesc rect)
|
||||
{
|
||||
// NOTE: Testing only.
|
||||
var ctx = new RenderingContext(_graphicsEngine, _commandBuffer);
|
||||
if (_frameIndex == 0)
|
||||
{
|
||||
_pass.Initialize(ref ctx);
|
||||
}
|
||||
|
||||
//_commandBuffer.BeginRenderPass(rtDesc, depthDesc, false);
|
||||
_commandBuffer.SetViewport(viewport);
|
||||
_commandBuffer.SetScissorRect(rect);
|
||||
|
||||
_renderGraph.Reset();
|
||||
|
||||
var backBuffer = _renderGraph.ImportTexture(target, "Back Buffer");
|
||||
_pass.Build(_renderGraph, backBuffer);
|
||||
|
||||
// Create view state from viewport
|
||||
var viewState = new ViewState((uint)viewport.Width, (uint)viewport.Height);
|
||||
|
||||
// Compile with view state
|
||||
_renderGraph.Compile(in viewState);
|
||||
_renderGraph.Execute(_commandBuffer);
|
||||
|
||||
//_commandBuffer.EndRenderPass();
|
||||
_frameIndex++;
|
||||
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Testing only.
|
||||
_pass.Cleanup(_resourceDatabase);
|
||||
_renderGraph.Dispose();
|
||||
|
||||
_commandBuffer.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,947 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
using static TerraFX.Aliases.D3D12MA_Alias;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
using static TerraFX.Interop.DirectX.D3D12MemAlloc;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal sealed unsafe partial class D3D12ResourceAllocator
|
||||
{
|
||||
// NOTE: _MAX_BYTES may not be accurate, we need to verify it with feature level checks.
|
||||
private const uint _MAX_BYTES = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u;
|
||||
private const uint _MAX_TEXTURE2D_DIMENSION = 16384u;
|
||||
private const uint _MAX_TEXTURE3D_DIMENSION = 2048u;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CheckBufferSize(ulong sizeInBytes)
|
||||
{
|
||||
if (sizeInBytes > _MAX_BYTES)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void CheckTexture2DSize(uint width, uint height)
|
||||
{
|
||||
if (width > _MAX_TEXTURE2D_DIMENSION || height > _MAX_TEXTURE2D_DIMENSION)
|
||||
{
|
||||
throw new InvalidOperationException($"ERROR: Texture size too large for DirectX 12 (width {width}, height {height})");
|
||||
}
|
||||
}
|
||||
|
||||
private static D3D12_SHADER_RESOURCE_VIEW_DESC CreateTextureSrvDesc(ID3D12Resource* pResource, uint mipLevels, uint arraySize, bool isCubeMap)
|
||||
{
|
||||
var resourceDesc = pResource->GetDesc();
|
||||
var srvDesc = new D3D12_SHADER_RESOURCE_VIEW_DESC
|
||||
{
|
||||
Format = resourceDesc.Format,
|
||||
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
|
||||
};
|
||||
|
||||
switch (resourceDesc.Dimension)
|
||||
{
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
|
||||
if (resourceDesc.DepthOrArraySize > 1)
|
||||
{
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
|
||||
srvDesc.Texture1DArray = new D3D12_TEX1D_ARRAY_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
ArraySize = arraySize,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE1D;
|
||||
srvDesc.Texture1D = new D3D12_TEX1D_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
|
||||
if (resourceDesc.DepthOrArraySize > 1)
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
||||
srvDesc.TextureCubeArray = new D3D12_TEXCUBE_ARRAY_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
NumCubes = arraySize / 6,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
srvDesc.ViewDimension = resourceDesc.SampleDesc.Count > 1 ? D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY : D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
srvDesc.Texture2DArray = new D3D12_TEX2D_ARRAY_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
ArraySize = arraySize,
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isCubeMap)
|
||||
{
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
srvDesc.TextureCube = new D3D12_TEXCUBE_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
srvDesc.ViewDimension = resourceDesc.SampleDesc.Count > 1 ? D3D12_SRV_DIMENSION_TEXTURE2DMS : D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D = new D3D12_TEX2D_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
|
||||
srvDesc.Texture3D = new D3D12_TEX3D_SRV
|
||||
{
|
||||
MipLevels = mipLevels,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported texture dimension for SRV: {resourceDesc.Dimension}");
|
||||
}
|
||||
|
||||
return srvDesc;
|
||||
}
|
||||
|
||||
private static D3D12_SHADER_RESOURCE_VIEW_DESC CreateBufferSrvDesc(ID3D12Resource* pResource, uint stride, bool isRaw)
|
||||
{
|
||||
var resourceDesc = pResource->GetDesc();
|
||||
var srvDesc = new D3D12_SHADER_RESOURCE_VIEW_DESC
|
||||
{
|
||||
ViewDimension = D3D12_SRV_DIMENSION_BUFFER,
|
||||
Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING
|
||||
};
|
||||
|
||||
if (isRaw)
|
||||
{
|
||||
srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
||||
srvDesc.Buffer.FirstElement = 0;
|
||||
srvDesc.Buffer.NumElements = (uint)(resourceDesc.Width / 4u);
|
||||
srvDesc.Buffer.StructureByteStride = 0;
|
||||
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW;
|
||||
}
|
||||
else // Assumes Structured
|
||||
{
|
||||
srvDesc.Format = resourceDesc.Format;
|
||||
srvDesc.Buffer.FirstElement = 0;
|
||||
srvDesc.Buffer.NumElements = (uint)(resourceDesc.Width / stride);
|
||||
srvDesc.Buffer.StructureByteStride = stride;
|
||||
srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE;
|
||||
}
|
||||
|
||||
return srvDesc;
|
||||
}
|
||||
|
||||
private static D3D12_RENDER_TARGET_VIEW_DESC CreateRtvDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, uint planeSlice = 0)
|
||||
{
|
||||
var resourceDesc = pResource->GetDesc();
|
||||
var rtvDesc = new D3D12_RENDER_TARGET_VIEW_DESC();
|
||||
|
||||
switch (resourceDesc.Dimension)
|
||||
{
|
||||
case D3D12_RESOURCE_DIMENSION_BUFFER:
|
||||
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_BUFFER;
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
|
||||
rtvDesc.ViewDimension = resourceDesc.DepthOrArraySize > 1 ? D3D12_RTV_DIMENSION_TEXTURE1DARRAY : D3D12_RTV_DIMENSION_TEXTURE1D;
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
|
||||
if (resourceDesc.SampleDesc.Count > 1)
|
||||
{
|
||||
rtvDesc.ViewDimension = resourceDesc.DepthOrArraySize > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY : D3D12_RTV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
rtvDesc.ViewDimension = resourceDesc.DepthOrArraySize > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DARRAY : D3D12_RTV_DIMENSION_TEXTURE2D;
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
|
||||
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported texture dimension for SRV: {resourceDesc.Dimension}");
|
||||
}
|
||||
|
||||
rtvDesc.Format = resourceDesc.Format;
|
||||
|
||||
var isArray =
|
||||
rtvDesc.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE2DARRAY ||
|
||||
rtvDesc.ViewDimension == D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
|
||||
|
||||
var arraySize = 1u;
|
||||
if (isArray)
|
||||
{
|
||||
arraySize = resourceDesc.ArraySize() - firstArraySlice;
|
||||
}
|
||||
|
||||
switch (rtvDesc.ViewDimension)
|
||||
{
|
||||
case D3D12_RTV_DIMENSION_BUFFER:
|
||||
rtvDesc.Buffer.FirstElement = firstArraySlice;
|
||||
rtvDesc.Buffer.NumElements = arraySize;
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE1D:
|
||||
rtvDesc.Texture1D.MipSlice = mipSlice;
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE1DARRAY:
|
||||
rtvDesc.Texture1DArray.MipSlice = mipSlice;
|
||||
rtvDesc.Texture1DArray.FirstArraySlice = firstArraySlice;
|
||||
rtvDesc.Texture1DArray.ArraySize = arraySize;
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE2D:
|
||||
rtvDesc.Texture2D.MipSlice = mipSlice;
|
||||
rtvDesc.Texture2D.PlaneSlice = planeSlice;
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE2DARRAY:
|
||||
rtvDesc.Texture2DArray.MipSlice = mipSlice;
|
||||
rtvDesc.Texture2DArray.FirstArraySlice = firstArraySlice;
|
||||
rtvDesc.Texture2DArray.ArraySize = arraySize;
|
||||
rtvDesc.Texture2DArray.PlaneSlice = planeSlice;
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE2DMS:
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY:
|
||||
rtvDesc.Texture2DMSArray.FirstArraySlice = firstArraySlice;
|
||||
rtvDesc.Texture2DMSArray.ArraySize = arraySize;
|
||||
break;
|
||||
|
||||
case D3D12_RTV_DIMENSION_TEXTURE3D:
|
||||
rtvDesc.Texture3D.MipSlice = mipSlice;
|
||||
rtvDesc.Texture3D.FirstWSlice = firstArraySlice;
|
||||
rtvDesc.Texture3D.WSize = arraySize;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported RTV dimension: {rtvDesc.ViewDimension}");
|
||||
}
|
||||
|
||||
return rtvDesc;
|
||||
}
|
||||
|
||||
private static D3D12_DEPTH_STENCIL_VIEW_DESC CreateDsvDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, D3D12_DSV_FLAGS flags = D3D12_DSV_FLAG_NONE)
|
||||
{
|
||||
var resourceDesc = pResource->GetDesc();
|
||||
var dsvDesc = new D3D12_DEPTH_STENCIL_VIEW_DESC
|
||||
{
|
||||
Flags = flags,
|
||||
};
|
||||
|
||||
switch (resourceDesc.Dimension)
|
||||
{
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
|
||||
dsvDesc.ViewDimension = resourceDesc.DepthOrArraySize > 1 ? D3D12_DSV_DIMENSION_TEXTURE1DARRAY : D3D12_DSV_DIMENSION_TEXTURE1D;
|
||||
break;
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
|
||||
if (resourceDesc.SampleDesc.Count > 1)
|
||||
{
|
||||
dsvDesc.ViewDimension = resourceDesc.DepthOrArraySize > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY : D3D12_DSV_DIMENSION_TEXTURE2DMS;
|
||||
}
|
||||
else
|
||||
{
|
||||
dsvDesc.ViewDimension = resourceDesc.DepthOrArraySize > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DARRAY : D3D12_DSV_DIMENSION_TEXTURE2D;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dsvDesc.Format = resourceDesc.Format;
|
||||
|
||||
var isArray =
|
||||
dsvDesc.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE2DARRAY ||
|
||||
dsvDesc.ViewDimension == D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY;
|
||||
|
||||
var arraySize = 1u;
|
||||
if (isArray)
|
||||
{
|
||||
arraySize = resourceDesc.ArraySize() - firstArraySlice;
|
||||
}
|
||||
|
||||
switch (dsvDesc.ViewDimension)
|
||||
{
|
||||
case D3D12_DSV_DIMENSION_TEXTURE1D:
|
||||
dsvDesc.Texture1D.MipSlice = mipSlice;
|
||||
break;
|
||||
case D3D12_DSV_DIMENSION_TEXTURE1DARRAY:
|
||||
dsvDesc.Texture1DArray.MipSlice = mipSlice;
|
||||
dsvDesc.Texture1DArray.FirstArraySlice = firstArraySlice;
|
||||
dsvDesc.Texture1DArray.ArraySize = arraySize;
|
||||
break;
|
||||
case D3D12_DSV_DIMENSION_TEXTURE2D:
|
||||
dsvDesc.Texture2D.MipSlice = mipSlice;
|
||||
break;
|
||||
case D3D12_DSV_DIMENSION_TEXTURE2DARRAY:
|
||||
dsvDesc.Texture2DArray.MipSlice = mipSlice;
|
||||
dsvDesc.Texture2DArray.FirstArraySlice = firstArraySlice;
|
||||
dsvDesc.Texture2DArray.ArraySize = arraySize;
|
||||
break;
|
||||
case D3D12_DSV_DIMENSION_TEXTURE2DMS:
|
||||
break;
|
||||
case D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY:
|
||||
dsvDesc.Texture2DMSArray.FirstArraySlice = firstArraySlice;
|
||||
dsvDesc.Texture2DMSArray.ArraySize = arraySize;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return dsvDesc;
|
||||
}
|
||||
|
||||
private static D3D12_UNORDERED_ACCESS_VIEW_DESC CreateTextureUavDesc(ID3D12Resource* pResource, uint mipSlice = 0, uint firstArraySlice = 0, uint planeSlice = 0)
|
||||
{
|
||||
var resourceDesc = pResource->GetDesc();
|
||||
var uavDesc = new D3D12_UNORDERED_ACCESS_VIEW_DESC
|
||||
{
|
||||
Format = resourceDesc.Format
|
||||
};
|
||||
|
||||
switch (resourceDesc.Dimension)
|
||||
{
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
|
||||
if (resourceDesc.DepthOrArraySize > 1)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1DARRAY;
|
||||
uavDesc.Texture1DArray = new D3D12_TEX1D_ARRAY_UAV
|
||||
{
|
||||
MipSlice = mipSlice,
|
||||
FirstArraySlice = firstArraySlice,
|
||||
ArraySize = resourceDesc.ArraySize() - firstArraySlice
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE1D;
|
||||
uavDesc.Texture1D = new D3D12_TEX1D_UAV
|
||||
{
|
||||
MipSlice = mipSlice
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
|
||||
if (resourceDesc.DepthOrArraySize > 1)
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
|
||||
uavDesc.Texture2DArray = new D3D12_TEX2D_ARRAY_UAV
|
||||
{
|
||||
MipSlice = mipSlice,
|
||||
FirstArraySlice = firstArraySlice,
|
||||
ArraySize = resourceDesc.ArraySize() - firstArraySlice,
|
||||
PlaneSlice = planeSlice
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;
|
||||
uavDesc.Texture2D = new D3D12_TEX2D_UAV
|
||||
{
|
||||
MipSlice = mipSlice,
|
||||
PlaneSlice = planeSlice
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D;
|
||||
uavDesc.Texture3D = new D3D12_TEX3D_UAV
|
||||
{
|
||||
MipSlice = mipSlice,
|
||||
FirstWSlice = firstArraySlice,
|
||||
WSize = resourceDesc.Depth() - firstArraySlice
|
||||
};
|
||||
break;
|
||||
|
||||
case D3D12_RESOURCE_DIMENSION_BUFFER:
|
||||
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
|
||||
uavDesc.Buffer = new D3D12_BUFFER_UAV
|
||||
{
|
||||
FirstElement = 0,
|
||||
NumElements = (uint)(resourceDesc.Width / 4), // Assuming R32_TYPELESS RAW
|
||||
StructureByteStride = 0,
|
||||
Flags = D3D12_BUFFER_UAV_FLAG_RAW
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported texture dimension for UAV: {resourceDesc.Dimension}");
|
||||
}
|
||||
|
||||
return uavDesc;
|
||||
}
|
||||
|
||||
private static D3D12_UNORDERED_ACCESS_VIEW_DESC CreateBufferUavDesc(ID3D12Resource* pResource, uint stride, bool isRaw)
|
||||
{
|
||||
var resourceDesc = pResource->GetDesc();
|
||||
var uavDesc = new D3D12_UNORDERED_ACCESS_VIEW_DESC
|
||||
{
|
||||
ViewDimension = D3D12_UAV_DIMENSION_BUFFER,
|
||||
};
|
||||
|
||||
if (isRaw)
|
||||
{
|
||||
uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
|
||||
uavDesc.Buffer.FirstElement = 0;
|
||||
uavDesc.Buffer.NumElements = (uint)(resourceDesc.Width / 4u);
|
||||
uavDesc.Buffer.StructureByteStride = 0;
|
||||
uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW;
|
||||
}
|
||||
else // Assumes Structured
|
||||
{
|
||||
uavDesc.Format = resourceDesc.Format;
|
||||
uavDesc.Buffer.FirstElement = 0;
|
||||
uavDesc.Buffer.NumElements = (uint)(resourceDesc.Width / stride);
|
||||
uavDesc.Buffer.StructureByteStride = stride;
|
||||
uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
|
||||
}
|
||||
|
||||
return uavDesc;
|
||||
}
|
||||
|
||||
private static D3D12_HEAP_TYPE ConvertMemoryType(ResourceMemoryType memoryType)
|
||||
{
|
||||
return memoryType switch
|
||||
{
|
||||
ResourceMemoryType.Default => D3D12_HEAP_TYPE_DEFAULT,
|
||||
ResourceMemoryType.Upload => D3D12_HEAP_TYPE_UPLOAD,
|
||||
ResourceMemoryType.Readback => D3D12_HEAP_TYPE_READBACK,
|
||||
_ => throw new ArgumentException($"Unsupported memory type: {memoryType}")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed unsafe partial class D3D12ResourceAllocator : IResourceAllocator
|
||||
{
|
||||
private const uint _UPLOAD_BATCH_SIZE = 64 * 1024 * 1024; // 64 MB
|
||||
private const uint _MAX_RESOURCE_SIZE_TO_FIT_IN_UPLOAD_BATCH = 16 * 1024 * 1024; // 16 MB
|
||||
|
||||
private UniquePtr<D3D12MA_Allocator> _d3d12MA;
|
||||
|
||||
private readonly D3D12RenderDevice _device;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12PipelineLibrary _pipelineLibrary;
|
||||
|
||||
// TODO: We should use ring buffer pool in d3d12ma for upload buffer.
|
||||
private readonly Handle<GraphicsBuffer> _uploadBatch;
|
||||
private ulong _uploadBatchOffset;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12ResourceAllocator(
|
||||
D3D12RenderDevice device,
|
||||
D3D12DescriptorAllocator descriptorAllocator,
|
||||
D3D12ResourceDatabase resourceDatabase,
|
||||
D3D12PipelineLibrary pipelineLibrary)
|
||||
{
|
||||
var desc = new D3D12MA_ALLOCATOR_DESC
|
||||
{
|
||||
pAdapter = (IDXGIAdapter*)device.Adapter.Get(),
|
||||
pDevice = (ID3D12Device*)device.NativeDevice.Get(),
|
||||
Flags = D3D12MA_ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED | D3D12MA_ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED,
|
||||
};
|
||||
|
||||
D3D12MA_Allocator* pAllocator = default;
|
||||
ThrowIfFailed(D3D12MA_CreateAllocator(&desc, &pAllocator));
|
||||
_d3d12MA.Attach(pAllocator);
|
||||
|
||||
_device = device;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_pipelineLibrary = pipelineLibrary;
|
||||
|
||||
// Create an upload batch
|
||||
var uploadDesc = new BufferDesc
|
||||
{
|
||||
Size = _UPLOAD_BATCH_SIZE,
|
||||
Usage = BufferUsage.Upload,
|
||||
MemoryType = ResourceMemoryType.Upload,
|
||||
};
|
||||
|
||||
_uploadBatch = CreateBuffer(in uploadDesc, "D3D12ResourceAllocator_UploadBatch");
|
||||
_uploadBatchOffset = 0;
|
||||
}
|
||||
|
||||
~D3D12ResourceAllocator()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private Handle<GPUResource> TrackAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData barrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string name, bool isTemp)
|
||||
{
|
||||
var handle = _resourceDatabase.AddAllocation(allocation, barrierData, resourceDescriptor, desc, name);
|
||||
return handle;
|
||||
}
|
||||
|
||||
private HRESULT CreateResource(D3D12MA_ALLOCATION_DESC* pAllocationDesc, D3D12_RESOURCE_DESC* pResourceDesc, D3D12_RESOURCE_STATES initialState, CreationOptions options, Guid* riid, void** ppv)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (options.AllocationType == ResourceAllocationType.Suballocation)
|
||||
{
|
||||
// pAllocation should be the render graph Heap. ppvResource should be the out resource.
|
||||
var result = _resourceDatabase.GetResourceRecord(options.Heap);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return E.E_NOTFOUND;
|
||||
}
|
||||
|
||||
hr = _d3d12MA.Get()->CreateAliasingResource(result.Value.resource.allocation.Get(), options.Offset, pResourceDesc, initialState, null, riid, ppv);
|
||||
}
|
||||
else
|
||||
{
|
||||
var iid_null = IID.IID_NULL;
|
||||
hr = _d3d12MA.Get()->CreateResource(pAllocationDesc, pResourceDesc, initialState, null, (D3D12MA_Allocation**)ppv, &iid_null, null);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
public ResourceSizeInfo GetSizeInfo(ResourceDesc desc)
|
||||
{
|
||||
D3D12_RESOURCE_DESC d3d12Desc;
|
||||
if (desc.Type == ResourceType.Texture)
|
||||
{
|
||||
d3d12Desc = desc.TextureDescription.ToD3D12ResourceDesc();
|
||||
}
|
||||
else
|
||||
{
|
||||
d3d12Desc = desc.BufferDescription.ToD3D12ResourceDesc();
|
||||
}
|
||||
|
||||
var info = _device.NativeDevice.Get()->GetResourceAllocationInfo(0, 1, &d3d12Desc);
|
||||
return new ResourceSizeInfo
|
||||
{
|
||||
Size = info.SizeInBytes,
|
||||
Alignment = info.Alignment
|
||||
};
|
||||
}
|
||||
|
||||
public Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string name)
|
||||
{
|
||||
var allocDesc = new D3D12MA_ALLOCATION_DESC
|
||||
{
|
||||
HeapType = desc.HeapType switch
|
||||
{
|
||||
HeapType.Default => D3D12_HEAP_TYPE_DEFAULT,
|
||||
HeapType.Upload => D3D12_HEAP_TYPE_UPLOAD,
|
||||
HeapType.Readback => D3D12_HEAP_TYPE_READBACK,
|
||||
_ => D3D12_HEAP_TYPE_DEFAULT
|
||||
},
|
||||
Flags = D3D12MA_ALLOCATION_FLAG_COMMITTED,
|
||||
ExtraHeapFlags = desc.HeapFlags switch
|
||||
{
|
||||
HeapFlags.None => D3D12_HEAP_FLAG_NONE,
|
||||
HeapFlags.AllowBuffers => D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,
|
||||
HeapFlags.AllowTextures => D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES,
|
||||
HeapFlags.AllowRTAndDS => D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES,
|
||||
HeapFlags.AlowBufferAndTexture => D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES,
|
||||
_ => D3D12_HEAP_FLAG_NONE
|
||||
}
|
||||
};
|
||||
|
||||
// SizeInBytes must be aligned to 64KB for committed resources
|
||||
var allocInfo = new D3D12_RESOURCE_ALLOCATION_INFO
|
||||
{
|
||||
SizeInBytes = desc.Size + 65535 & ~65535u,
|
||||
Alignment = desc.Alignment
|
||||
};
|
||||
|
||||
D3D12MA_Allocation* alloc = default;
|
||||
if (_d3d12MA.Get()->AllocateMemory(&allocDesc, &allocInfo, &alloc).FAILED)
|
||||
{
|
||||
return Handle<GPUResource>.Invalid;
|
||||
}
|
||||
|
||||
var barrierData = new ResourceBarrierData
|
||||
{
|
||||
access = BarrierAccess.NoAccess,
|
||||
layout = BarrierLayout.Common,
|
||||
sync = BarrierSync.None
|
||||
};
|
||||
|
||||
return TrackAllocation(alloc, barrierData, ResourceViewGroup.Invalid, default, name, false);
|
||||
}
|
||||
|
||||
public Handle<Texture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
CheckTexture2DSize(desc.Width, desc.Height);
|
||||
|
||||
var resourceDesc = desc.ToD3D12ResourceDesc();
|
||||
var allocationDesc = new D3D12MA_ALLOCATION_DESC
|
||||
{
|
||||
HeapType = D3D12_HEAP_TYPE_DEFAULT,
|
||||
Flags = D3D12MA_ALLOCATION_FLAG_NONE
|
||||
};
|
||||
|
||||
var isSubAllocation = options.AllocationType == ResourceAllocationType.Suballocation;
|
||||
D3D12MA_Allocation* pAllocation = default;
|
||||
ID3D12Resource* pResource = default;
|
||||
HRESULT hr;
|
||||
|
||||
if (isSubAllocation)
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_RESOURCE_STATE_COMMON, options, __uuidof(pResource), (void**)&pResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, D3D12_RESOURCE_STATE_COMMON, options, null, (void**)&pAllocation);
|
||||
pResource = pAllocation->GetResource();
|
||||
}
|
||||
|
||||
if (hr.FAILED)
|
||||
{
|
||||
#if DEBUG
|
||||
ThrowIfFailed(hr);
|
||||
#endif
|
||||
return Handle<Texture>.Invalid;
|
||||
}
|
||||
|
||||
var isTemp = options.AllocationType == ResourceAllocationType.Temporary;
|
||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||
if (desc.Usage.HasFlag(TextureUsage.ShaderResource))
|
||||
{
|
||||
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
||||
|
||||
var isCubeMap = desc.Dimension == TextureDimension.TextureCube || desc.Dimension == TextureDimension.TextureCubeArray;
|
||||
var srvDesc = CreateTextureSrvDesc(pResource, resourceDesc.MipLevels, resourceDesc.DepthOrArraySize, isCubeMap);
|
||||
|
||||
_device.NativeDevice.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.srv);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(TextureUsage.RenderTarget))
|
||||
{
|
||||
resourceDescriptor.rtv = _descriptorAllocator.AllocateRTV();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.rtv);
|
||||
var rtvDesc = CreateRtvDesc(pResource);
|
||||
|
||||
_device.NativeDevice.Get()->CreateRenderTargetView(pResource, &rtvDesc, cpuHandle);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(TextureUsage.DepthStencil))
|
||||
{
|
||||
resourceDescriptor.dsv = _descriptorAllocator.AllocateDSV();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.dsv);
|
||||
var dsvDesc = CreateDsvDesc(pResource);
|
||||
|
||||
_device.NativeDevice.Get()->CreateDepthStencilView(pResource, &dsvDesc, cpuHandle);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||
{
|
||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||
var uavDesc = CreateTextureUavDesc(pResource);
|
||||
|
||||
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav);
|
||||
}
|
||||
|
||||
var barrierData = new ResourceBarrierData
|
||||
{
|
||||
access = BarrierAccess.NoAccess,
|
||||
layout = BarrierLayout.Common,
|
||||
sync = BarrierSync.None
|
||||
};
|
||||
|
||||
Handle<GPUResource> resource;
|
||||
if (isSubAllocation)
|
||||
{
|
||||
resource = _resourceDatabase.ImportExternalResource(pResource, barrierData, resourceDescriptor, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
resource = TrackAllocation(pAllocation, barrierData, resourceDescriptor, ResourceDesc.Texture(desc), name, isTemp);
|
||||
}
|
||||
|
||||
return resource.AsTexture();
|
||||
}
|
||||
|
||||
public Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var textureDesc = desc.ToTextureDescription();
|
||||
return CreateTexture(in textureDesc, name, options);
|
||||
}
|
||||
|
||||
public Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
CheckBufferSize(desc.Size);
|
||||
|
||||
var resourceDesc = desc.ToD3D12ResourceDesc();
|
||||
var isRaw = desc.Usage.HasFlag(BufferUsage.Raw);
|
||||
|
||||
var allocationDesc = new D3D12MA_ALLOCATION_DESC
|
||||
{
|
||||
HeapType = ConvertMemoryType(desc.MemoryType),
|
||||
Flags = D3D12MA_ALLOCATION_FLAG_NONE,
|
||||
};
|
||||
|
||||
var isSubAllocation = options.Heap.IsValid;
|
||||
D3D12MA_Allocation* pAllocation = default;
|
||||
ID3D12Resource* pResource = default;
|
||||
HRESULT hr;
|
||||
|
||||
var initialState = desc.MemoryType switch
|
||||
{
|
||||
ResourceMemoryType.Default => D3D12_RESOURCE_STATE_COMMON,
|
||||
ResourceMemoryType.Upload => D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
ResourceMemoryType.Readback => D3D12_RESOURCE_STATE_COPY_DEST,
|
||||
_ => D3D12_RESOURCE_STATE_COMMON
|
||||
};
|
||||
|
||||
if (isSubAllocation)
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, initialState, options, __uuidof(pResource), (void**)&pResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = CreateResource(&allocationDesc, &resourceDesc, initialState, options, null, (void**)&pAllocation);
|
||||
pResource = pAllocation->GetResource();
|
||||
}
|
||||
|
||||
if (hr.FAILED)
|
||||
{
|
||||
#if DEBUG
|
||||
ThrowIfFailed(hr);
|
||||
#endif
|
||||
return Handle<GraphicsBuffer>.Invalid;
|
||||
}
|
||||
|
||||
var isTemp = options.AllocationType == ResourceAllocationType.Temporary;
|
||||
var resourceDescriptor = ResourceViewGroup.Invalid;
|
||||
|
||||
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
||||
{
|
||||
resourceDescriptor.cbv = _descriptorAllocator.AllocateCbvSrvUav();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.cbv);
|
||||
var cbvDesc = new D3D12_CONSTANT_BUFFER_VIEW_DESC
|
||||
{
|
||||
BufferLocation = pResource->GetGPUVirtualAddress(),
|
||||
SizeInBytes = (uint)resourceDesc.Width,
|
||||
};
|
||||
|
||||
_device.NativeDevice.Get()->CreateConstantBufferView(&cbvDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.cbv);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(BufferUsage.ShaderResource))
|
||||
{
|
||||
resourceDescriptor.srv = _descriptorAllocator.AllocateCbvSrvUav();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.srv);
|
||||
var srvDesc = CreateBufferSrvDesc(pResource, desc.Stride, isRaw);
|
||||
|
||||
_device.NativeDevice.Get()->CreateShaderResourceView(pResource, &srvDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.srv);
|
||||
}
|
||||
|
||||
if (desc.Usage.HasFlag(BufferUsage.UnorderedAccess))
|
||||
{
|
||||
resourceDescriptor.uav = _descriptorAllocator.AllocateCbvSrvUav();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(resourceDescriptor.uav);
|
||||
var uavDesc = CreateBufferUavDesc(pResource, desc.Stride, isRaw);
|
||||
|
||||
_device.NativeDevice.Get()->CreateUnorderedAccessView(pResource, null, &uavDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(resourceDescriptor.uav);
|
||||
}
|
||||
|
||||
var barrierData = new ResourceBarrierData
|
||||
{
|
||||
access = BarrierAccess.NoAccess,
|
||||
layout = BarrierLayout.Undefined,
|
||||
sync = BarrierSync.None
|
||||
};
|
||||
|
||||
Handle<GPUResource> resource;
|
||||
if (isSubAllocation)
|
||||
{
|
||||
resource = _resourceDatabase.ImportExternalResource(pResource, barrierData, resourceDescriptor, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
resource = TrackAllocation(pAllocation, barrierData, resourceDescriptor, ResourceDesc.Buffer(desc), name, isTemp);
|
||||
}
|
||||
|
||||
return resource.AsGraphicsBuffer();
|
||||
}
|
||||
|
||||
public Handle<GraphicsBuffer> CreateTempUploadBuffer(ulong sizeInBytes, out ulong offset)
|
||||
{
|
||||
if (sizeInBytes <= _MAX_RESOURCE_SIZE_TO_FIT_IN_UPLOAD_BATCH && sizeInBytes + _uploadBatchOffset <= _UPLOAD_BATCH_SIZE)
|
||||
{
|
||||
offset = _uploadBatchOffset;
|
||||
_uploadBatchOffset += sizeInBytes;
|
||||
return _uploadBatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
var bufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)sizeInBytes,
|
||||
Usage = BufferUsage.Upload,
|
||||
MemoryType = ResourceMemoryType.Upload,
|
||||
};
|
||||
|
||||
var options = new CreationOptions
|
||||
{
|
||||
AllocationType = ResourceAllocationType.Temporary,
|
||||
};
|
||||
|
||||
offset = 0;
|
||||
var handle = CreateBuffer(in bufferDesc, "TempUploadBuffer", options);
|
||||
|
||||
_resourceDatabase.ScheduleReleaseResource(handle.AsResource());
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_resourceDatabase.TryGetSampler(in desc, out var id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
var samplerDesc = new D3D12_SAMPLER_DESC
|
||||
{
|
||||
Filter = desc.FilterMode.ToD3D12Filter(),
|
||||
AddressU = desc.AddressU.ToD3D12TextureAddressMode(),
|
||||
AddressV = desc.AddressV.ToD3D12TextureAddressMode(),
|
||||
AddressW = desc.AddressW.ToD3D12TextureAddressMode(),
|
||||
MipLODBias = desc.MipLODBias,
|
||||
MaxAnisotropy = desc.MaxAnisotropy,
|
||||
ComparisonFunc = desc.ComparisonFunc.ToD3D12ComparisonFunc(),
|
||||
MinLOD = desc.MinLOD,
|
||||
MaxLOD = desc.MaxLOD,
|
||||
};
|
||||
|
||||
var samplerDescriptor = _descriptorAllocator.AllocateSampler();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(samplerDescriptor);
|
||||
_device.NativeDevice.Get()->CreateSampler(&samplerDesc, cpuHandle);
|
||||
_descriptorAllocator.CopyToShaderVisible(samplerDescriptor);
|
||||
|
||||
return _resourceDatabase.CreateSampler(in desc, samplerDescriptor.Value);
|
||||
}
|
||||
|
||||
public Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(vertices.Count * sizeof(Vertex)),
|
||||
Stride = (uint)sizeof(Vertex),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var indexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(indices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var objectBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)sizeof(PerObjectData),
|
||||
Stride = (uint)sizeof(PerObjectData),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var vertexBuffer = CreateBuffer(in vertexBufferDesc, "VertexBuffer");
|
||||
var indexBuffer = CreateBuffer(in indexBufferDesc, "IndexBuffer");
|
||||
var objectBuffer = CreateBuffer(in objectBufferDesc, "ObjectBuffer");
|
||||
|
||||
var data = new Mesh
|
||||
{
|
||||
Vertices = vertices,
|
||||
Indices = indices,
|
||||
VertexBuffer = vertexBuffer,
|
||||
IndexBuffer = indexBuffer,
|
||||
ObjectDataBuffer = objectBuffer,
|
||||
};
|
||||
|
||||
return _resourceDatabase.AddMesh(in data);
|
||||
}
|
||||
|
||||
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var material = new Material();
|
||||
if (material.SetShader(shader, this, _resourceDatabase) != Error.None)
|
||||
{
|
||||
return Handle<Material>.Invalid;
|
||||
}
|
||||
|
||||
return _resourceDatabase.AddMaterial(in material);
|
||||
}
|
||||
|
||||
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var shader = new Shader(descriptor);
|
||||
return _resourceDatabase.AddShader(shader);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_resourceDatabase.ReleaseResourceImmediately(_uploadBatch.AsResource());
|
||||
_d3d12MA.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,535 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Collections;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
// TODO: Thread safety
|
||||
internal class D3D12ResourceDatabase : IResourceDatabase
|
||||
{
|
||||
internal unsafe record struct ResourceRecord
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ResourceUnion
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public UniquePtr<D3D12MA_Allocation> allocation;
|
||||
[FieldOffset(0)]
|
||||
public UniquePtr<ID3D12Resource> resource;
|
||||
|
||||
public ResourceUnion(D3D12MA_Allocation* allocation)
|
||||
{
|
||||
this.allocation = allocation;
|
||||
}
|
||||
|
||||
public ResourceUnion(ID3D12Resource* resource)
|
||||
{
|
||||
this.resource = resource;
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceDesc desc;
|
||||
public ResourceViewGroup viewGroup;
|
||||
public ResourceUnion resource;
|
||||
|
||||
public ResourceBarrierData barrierData;
|
||||
|
||||
public readonly bool isExternal;
|
||||
|
||||
public readonly bool Allocated => isExternal ? resource.resource.Get() != null : resource.allocation.Get() != null;
|
||||
public readonly SharedPtr<ID3D12Resource> ResourcePtr => isExternal ? resource.resource.Get() : resource.allocation.Get()->GetResource();
|
||||
|
||||
public ResourceRecord(D3D12MA_Allocation* allocation, ResourceBarrierData barrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc)
|
||||
{
|
||||
this.resource = new ResourceUnion(allocation);
|
||||
this.isExternal = false;
|
||||
|
||||
this.viewGroup = resourceDescriptor;
|
||||
this.barrierData = barrierData;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public ResourceRecord(ID3D12Resource* resource, ResourceBarrierData barrierData, ResourceViewGroup viewGroup)
|
||||
{
|
||||
this.resource = new ResourceUnion(resource);
|
||||
this.isExternal = true;
|
||||
|
||||
this.viewGroup = viewGroup;
|
||||
this.barrierData = barrierData;
|
||||
this.desc = resource->GetDesc().ToResourceDesc();
|
||||
}
|
||||
|
||||
public readonly uint Release(D3D12DescriptorAllocator descriptorAllocator)
|
||||
{
|
||||
var refCount = 0u;
|
||||
if (Allocated)
|
||||
{
|
||||
if (isExternal)
|
||||
{
|
||||
refCount = resource.resource.Get()->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
refCount = resource.allocation.Get()->Release();
|
||||
}
|
||||
}
|
||||
|
||||
descriptorAllocator.Release(viewGroup);
|
||||
return refCount;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct ReleaseEntry
|
||||
{
|
||||
public readonly ResourceRecord record;
|
||||
public readonly uint fenceValue;
|
||||
|
||||
public ReleaseEntry(ResourceRecord record, uint fenceValue)
|
||||
{
|
||||
this.record = record;
|
||||
this.fenceValue = fenceValue;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly IFenceSynchronizer _fenceSynchronizer;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
|
||||
private UnsafeSlotMap<ResourceRecord> _resources;
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
private readonly Dictionary<Handle<GPUResource>, string> _resourceName;
|
||||
#endif
|
||||
|
||||
private UnsafeHashMap<SamplerDesc, Identifier<Sampler>> _samplers;
|
||||
private UnsafeSlotMap<Mesh> _meshes;
|
||||
private UnsafeSlotMap<Material> _materials;
|
||||
private readonly DynamicArray<Shader> _shaders; // TODO: Use SlotMap?
|
||||
|
||||
private UnsafeQueue<ReleaseEntry> _releaseQueue;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public D3D12ResourceDatabase(IFenceSynchronizer fenceSynchronizer, D3D12DescriptorAllocator descriptorAllocator)
|
||||
{
|
||||
_fenceSynchronizer = fenceSynchronizer;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
|
||||
_resources = new UnsafeSlotMap<ResourceRecord>(64, Allocator.Persistent, AllocationOption.Clear);
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
_resourceName = new Dictionary<Handle<GPUResource>, string>(64);
|
||||
#endif
|
||||
_samplers = new UnsafeHashMap<SamplerDesc, Identifier<Sampler>>(32, Allocator.Persistent);
|
||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent, AllocationOption.Clear);
|
||||
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent, AllocationOption.Clear);
|
||||
_shaders = new DynamicArray<Shader>(16);
|
||||
|
||||
_releaseQueue = new UnsafeQueue<ReleaseEntry>(32, Allocator.Persistent);
|
||||
}
|
||||
|
||||
~D3D12ResourceDatabase()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void ReleaseResource<T>(T resource)
|
||||
where T : IResourceReleasable
|
||||
{
|
||||
resource.ReleaseResource(this);
|
||||
}
|
||||
|
||||
internal unsafe Handle<GPUResource> ImportExternalResource(ID3D12Resource* pResource, ResourceBarrierData initialBarrierData, ResourceViewGroup viewGroup, string? name = null)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (pResource == null)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debugger.Break();
|
||||
#endif
|
||||
return Handle<GPUResource>.Invalid;
|
||||
}
|
||||
|
||||
var id = _resources.Add(new ResourceRecord(pResource, initialBarrierData, viewGroup), out var generation);
|
||||
var handle = new Handle<GPUResource>(id, generation);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
pResource->SetName(name);
|
||||
_resourceName[handle] = name;
|
||||
}
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public unsafe Handle<GPUResource> AddAllocation(D3D12MA_Allocation* allocation, ResourceBarrierData initialBarrierData, ResourceViewGroup resourceDescriptor, ResourceDesc desc, string? name = null)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
if (allocation == null)
|
||||
{
|
||||
#if DEBUG
|
||||
System.Diagnostics.Debugger.Break();
|
||||
#endif
|
||||
return Handle<GPUResource>.Invalid;
|
||||
}
|
||||
|
||||
var id = _resources.Add(new ResourceRecord(allocation, initialBarrierData, resourceDescriptor, desc), out var generation);
|
||||
var handle = new Handle<GPUResource>(id, generation);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
allocation->SetName(name);
|
||||
var pResource = allocation->GetResource();
|
||||
if (pResource != null)
|
||||
{
|
||||
pResource->SetName(name);
|
||||
}
|
||||
_resourceName[handle] = name;
|
||||
}
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
public bool HasResource(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _resources.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public RefResult<ResourceRecord, Error> GetResourceRecord(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<ResourceRecord, Error>.Success(ref info);
|
||||
}
|
||||
|
||||
public SharedPtr<ID3D12Resource> GetResource(Handle<GPUResource> handle)
|
||||
{
|
||||
var r = GetResourceRecord(handle);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return r.Value.ResourcePtr;
|
||||
}
|
||||
|
||||
public Result<ResourceBarrierData, Error> GetResourceBarrierData(Handle<GPUResource> handle)
|
||||
{
|
||||
var r = GetResourceRecord(handle);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r.Error;
|
||||
}
|
||||
|
||||
return r.Value.barrierData;
|
||||
}
|
||||
|
||||
public Error SetResourceBarrierData(Handle<GPUResource> handle, ResourceBarrierData data)
|
||||
{
|
||||
var r = GetResourceRecord(handle);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r.Error;
|
||||
}
|
||||
|
||||
r.Value.barrierData = data;
|
||||
return Error.None;
|
||||
}
|
||||
|
||||
public Result<ResourceDesc, Error> GetResourceDescription(Handle<GPUResource> handle)
|
||||
{
|
||||
var r = GetResourceRecord(handle);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return r.Error;
|
||||
}
|
||||
|
||||
return r.Value.desc;
|
||||
}
|
||||
|
||||
public uint GetBindlessIndex(Handle<GPUResource> handle, BindlessAccess access = BindlessAccess.ShaderResource)
|
||||
{
|
||||
var r = GetResourceRecord(handle);
|
||||
if (r.IsFailure || !r.Value.Allocated)
|
||||
{
|
||||
return ~0u;
|
||||
}
|
||||
|
||||
return access switch
|
||||
{
|
||||
BindlessAccess.ShaderResource => (uint)r.Value.viewGroup.srv.Value,
|
||||
BindlessAccess.ConstantBuffer => (uint)r.Value.viewGroup.cbv.Value,
|
||||
BindlessAccess.UnorderedAccess => (uint)r.Value.viewGroup.uav.Value,
|
||||
_ => ~0u,
|
||||
};
|
||||
}
|
||||
|
||||
public string? GetResourceName(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (_resourceName.TryGetValue(handle, out var name))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ScheduleReleaseResource(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_resources.TryGetElementAt(handle.ID, handle.Generation, out var record))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var entry = new ReleaseEntry(record, _fenceSynchronizer.CPUFenceValue);
|
||||
|
||||
_releaseQueue.Enqueue(entry);
|
||||
_resources.Remove(handle.ID, handle.Generation);
|
||||
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
_resourceName.Remove(handle, out var name);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ReleaseResourceImmediately(Handle<GPUResource> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var info = ref _resources.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist || !info.Allocated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info.Release(_descriptorAllocator);
|
||||
_resources.Remove(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc, int id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (_samplers.ContainsKey(desc))
|
||||
{
|
||||
throw new InvalidOperationException("Sampler already exists.");
|
||||
}
|
||||
|
||||
var identifier = new Identifier<Sampler>(id);
|
||||
_samplers.Add(desc, identifier);
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _samplers.TryGetValue(desc, out id);
|
||||
}
|
||||
|
||||
public void ReleaseSampler(Identifier<Sampler> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
// NOTE: We almost never release samplers individually, because they are cheap and can be reused.
|
||||
// Ideally we would release all samplers at once when disposing the ResourceDatabase.
|
||||
_descriptorAllocator.Release(new Identifier<SamplerDescriptor>(id.Value));
|
||||
}
|
||||
|
||||
public Handle<Mesh> AddMesh(ref readonly Mesh mesh)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = _meshes.Add(mesh, out var generation);
|
||||
return new Handle<Mesh>(id, generation);
|
||||
}
|
||||
|
||||
public bool HasMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _meshes.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle)
|
||||
{
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Mesh, Error>.Success(ref mesh);
|
||||
}
|
||||
|
||||
public void ReleaseMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseResource(mesh);
|
||||
_meshes.Remove(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public Handle<Material> AddMaterial(ref readonly Material material)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = _materials.Add(material, out var generation);
|
||||
return new Handle<Material>(id, generation);
|
||||
}
|
||||
|
||||
public bool HasMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _materials.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public RefResult<Material, Error> GetMaterialReference(Handle<Material> handle)
|
||||
{
|
||||
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Material, Error>.Success(ref material);
|
||||
}
|
||||
|
||||
public void ReleaseMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseResource(material);
|
||||
_materials.Remove(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public Identifier<Shader> AddShader(Shader shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var id = _shaders.Count;
|
||||
_shaders.Add(shader);
|
||||
return new Identifier<Shader>(id);
|
||||
}
|
||||
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return id.Value >= 0 && id.Value < _shaders.Count;
|
||||
}
|
||||
|
||||
public RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Shader, Error>.Success(ref _shaders[id.Value]);
|
||||
}
|
||||
|
||||
public void ReleaseShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!HasShader(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var shader = ref _shaders[id.Value]!;
|
||||
ReleaseResource(shader);
|
||||
}
|
||||
|
||||
public void EndFrame()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
while (_releaseQueue.Count > 0)
|
||||
{
|
||||
var toRelease = _releaseQueue.Peek();
|
||||
if (toRelease.fenceValue > _fenceSynchronizer.GPUFenceValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_releaseQueue.Dequeue();
|
||||
|
||||
toRelease.record.Release(_descriptorAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReleaseAllResourcesImmediately()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
foreach (var mesh in _meshes)
|
||||
{
|
||||
ReleaseResource(mesh);
|
||||
}
|
||||
|
||||
foreach (var material in _materials)
|
||||
{
|
||||
ReleaseResource(material);
|
||||
}
|
||||
|
||||
foreach (var shader in _shaders)
|
||||
{
|
||||
ReleaseResource(shader);
|
||||
}
|
||||
|
||||
foreach (ref var record in _resources)
|
||||
{
|
||||
record.Release(_descriptorAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_resources.Dispose();
|
||||
_samplers.Dispose();
|
||||
_meshes.Dispose();
|
||||
_materials.Dispose();
|
||||
_releaseQueue.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
/// <summary>
|
||||
/// D3D12 implementation of swap chain interface
|
||||
/// </summary>
|
||||
internal unsafe class D3D12SwapChain : ISwapChain
|
||||
{
|
||||
private readonly D3D12ResourceDatabase _resourceDatabase;
|
||||
private readonly D3D12DescriptorAllocator _descriptorAllocator;
|
||||
private readonly D3D12RenderDevice _renderDevice;
|
||||
|
||||
private UniquePtr<IDXGISwapChain4> _swapChain;
|
||||
private UnsafeArray<Handle<Texture>> _backBuffers;
|
||||
|
||||
private readonly object? _compositionSurface;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public uint Width
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public uint Height
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public float ScaleX
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public float ScaleY
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public D3D12SwapChain(D3D12ResourceDatabase resourceDatabase, D3D12DescriptorAllocator descriptorAllocator, D3D12RenderDevice device, SwapChainDesc desc, uint bufferCount)
|
||||
{
|
||||
Debug.Assert(bufferCount >= 2);
|
||||
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_descriptorAllocator = descriptorAllocator;
|
||||
_renderDevice = device;
|
||||
|
||||
_backBuffers = new UnsafeArray<Handle<Texture>>((int)bufferCount, Allocator.Persistent);
|
||||
|
||||
Width = desc.Width;
|
||||
Height = desc.Height;
|
||||
|
||||
var pSwapChian = CreateSwapChain(desc, bufferCount);
|
||||
_swapChain.Attach(pSwapChian);
|
||||
|
||||
CreateBackBuffers();
|
||||
SetScale(desc.ScaleX, desc.ScaleY);
|
||||
|
||||
if (desc.Target.Type == SwapChainTargetType.Composition)
|
||||
_compositionSurface = desc.Target.CompositionSurface;
|
||||
}
|
||||
|
||||
~D3D12SwapChain()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private IDXGISwapChain4* CreateSwapChain(SwapChainDesc desc, uint bufferCount)
|
||||
{
|
||||
var swapChainDesc = new DXGI_SWAP_CHAIN_DESC1
|
||||
{
|
||||
Width = desc.Width,
|
||||
Height = desc.Height,
|
||||
Format = desc.Format.ToDXGIFormat(),
|
||||
SampleDesc = new DXGI_SAMPLE_DESC(1, 0),
|
||||
BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
BufferCount = bufferCount,
|
||||
Scaling = DXGI_SCALING_STRETCH,
|
||||
SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
|
||||
AlphaMode = DXGI_ALPHA_MODE_IGNORE,
|
||||
Flags = (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING,
|
||||
Stereo = false,
|
||||
};
|
||||
|
||||
IDXGISwapChain1* pTempSwapChain = default;
|
||||
|
||||
var pFactory = _renderDevice.DXGIFactory.Get();
|
||||
var pCommandQueue = _renderDevice.NativeGraphicsQueue.Get();
|
||||
|
||||
switch (desc.Target.Type)
|
||||
{
|
||||
case SwapChainTargetType.Composition:
|
||||
ThrowIfFailed(pFactory->CreateSwapChainForComposition((IUnknown*)pCommandQueue, &swapChainDesc, null, &pTempSwapChain));
|
||||
|
||||
if (desc.Target.CompositionSurface != null)
|
||||
{
|
||||
using var compositionSurface = ISwapChainPanelNative.FromSwapChainPanel(desc.Target.CompositionSurface);
|
||||
compositionSurface.SetSwapChain((nint)pTempSwapChain);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SwapChainTargetType.WindowHandle:
|
||||
var swapChainFullscreenDesc = new DXGI_SWAP_CHAIN_FULLSCREEN_DESC
|
||||
{
|
||||
Windowed = true,
|
||||
};
|
||||
|
||||
pFactory->CreateSwapChainForHwnd(
|
||||
(IUnknown*)pCommandQueue,
|
||||
new HWND(desc.Target.WindowHandle.ToPointer()),
|
||||
&swapChainDesc,
|
||||
&swapChainFullscreenDesc,
|
||||
null,
|
||||
&pTempSwapChain);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Unsupported swap chain target type.");
|
||||
}
|
||||
|
||||
IDXGISwapChain4* pSwapChain = default;
|
||||
pTempSwapChain->QueryInterface(__uuidof(pSwapChain), (void**)&pSwapChain);
|
||||
pTempSwapChain->Release();
|
||||
|
||||
return pSwapChain;
|
||||
}
|
||||
|
||||
private void CreateBackBuffers()
|
||||
{
|
||||
for (uint i = 0; i < _backBuffers.Count; i++)
|
||||
{
|
||||
ID3D12Resource* pBackBuffer = default;
|
||||
ThrowIfFailed(_swapChain.Get()->GetBuffer(i, __uuidof(pBackBuffer), (void**)&pBackBuffer));
|
||||
pBackBuffer->SetName($"SwapChain_BackBuffer_{i}");
|
||||
|
||||
var rtv = _descriptorAllocator.AllocateRTV();
|
||||
var cpuHandle = _descriptorAllocator.GetCpuHandle(rtv);
|
||||
_renderDevice.NativeDevice.Get()->CreateRenderTargetView(pBackBuffer, null, cpuHandle);
|
||||
|
||||
var view = ResourceViewGroup.Invalid with
|
||||
{
|
||||
rtv = rtv
|
||||
};
|
||||
|
||||
var barrierData = new ResourceBarrierData
|
||||
{
|
||||
access = BarrierAccess.NoAccess,
|
||||
layout = BarrierLayout.Present,
|
||||
sync = BarrierSync.None,
|
||||
};
|
||||
|
||||
var handle = _resourceDatabase.ImportExternalResource(pBackBuffer, barrierData, view);
|
||||
_backBuffers[i] = handle.AsTexture();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Handle<Texture> GetCurrentBackBuffer()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _backBuffers[_swapChain.Get()->GetCurrentBackBufferIndex()];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<Handle<Texture>> GetBackBuffers()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _backBuffers.AsSpan();
|
||||
}
|
||||
|
||||
public void Present(bool vsync = true)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var presentFlags = 0u;
|
||||
var syncInterval = vsync ? 1u : 0u;
|
||||
|
||||
ThrowIfFailed(_swapChain.Get()->Present(syncInterval, presentFlags));
|
||||
}
|
||||
|
||||
public void Resize(uint width, uint height)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (Width == width && Height == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Release old back buffers and render targets
|
||||
for (var i = 0; i < _backBuffers.Count; i++)
|
||||
{
|
||||
_resourceDatabase.ReleaseResourceImmediately(_backBuffers[i].AsResource());
|
||||
}
|
||||
|
||||
ThrowIfFailed(_swapChain.Get()->ResizeBuffers((uint)_backBuffers.Count, width, height, DXGI_FORMAT_B8G8R8A8_UNORM, (uint)DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING));
|
||||
|
||||
Width = width;
|
||||
Height = height;
|
||||
|
||||
CreateBackBuffers();
|
||||
}
|
||||
|
||||
public void SetScale(float scaleX, float scaleY)
|
||||
{
|
||||
var inverseScaleX = 1.0f / scaleX;
|
||||
var inverseScaleY = 1.0f / scaleY;
|
||||
|
||||
var inverseScaleMatrix = new DXGI_MATRIX_3X2_F
|
||||
{
|
||||
_11 = inverseScaleX, // Scale X
|
||||
_22 = inverseScaleY, // Scale Y
|
||||
_12 = 0.0f,
|
||||
_21 = 0.0f,
|
||||
_31 = 0.0f, // Offset X
|
||||
_32 = 0.0f // Offset Y
|
||||
};
|
||||
|
||||
_swapChain.Get()->SetMatrixTransform(&inverseScaleMatrix);
|
||||
|
||||
ScaleX = scaleX;
|
||||
ScaleY = scaleY;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_compositionSurface != null)
|
||||
{
|
||||
using var compositionSurface = ISwapChainPanelNative.FromSwapChainPanel(_compositionSurface);
|
||||
compositionSurface.SetSwapChain(0);
|
||||
}
|
||||
|
||||
for (var i = 0; i < _backBuffers.Count; i++)
|
||||
{
|
||||
_resourceDatabase.ScheduleReleaseResource(_backBuffers[i].AsResource());
|
||||
}
|
||||
|
||||
_backBuffers.Dispose();
|
||||
_swapChain.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Graphics.D3D12;
|
||||
|
||||
internal readonly struct RTVDescriptor;
|
||||
internal readonly struct DSVDescriptor;
|
||||
internal readonly struct CbvSrvUavDescriptor;
|
||||
internal readonly struct SamplerDescriptor;
|
||||
|
||||
internal struct ResourceViewGroup
|
||||
{
|
||||
public Identifier<RTVDescriptor> rtv;
|
||||
public Identifier<DSVDescriptor> dsv;
|
||||
public Identifier<CbvSrvUavDescriptor> srv;
|
||||
public Identifier<CbvSrvUavDescriptor> cbv;
|
||||
public Identifier<CbvSrvUavDescriptor> uav;
|
||||
public Identifier<SamplerDescriptor> sampler;
|
||||
|
||||
public static ResourceViewGroup Invalid => new()
|
||||
{
|
||||
rtv = Identifier<RTVDescriptor>.Invalid,
|
||||
dsv = Identifier<DSVDescriptor>.Invalid,
|
||||
srv = Identifier<CbvSrvUavDescriptor>.Invalid,
|
||||
cbv = Identifier<CbvSrvUavDescriptor>.Invalid,
|
||||
uav = Identifier<CbvSrvUavDescriptor>.Invalid,
|
||||
sampler = Identifier<SamplerDescriptor>.Invalid,
|
||||
};
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using Ghost.Graphics.Core;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal unsafe static class D3D12PipelineResource
|
||||
{
|
||||
private readonly static D3D12_INPUT_ELEMENT_DESC[] s_inputElementDescs = [
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Position.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Normal.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Tangent.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Uv.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
new D3D12_INPUT_ELEMENT_DESC{ SemanticName = (sbyte*)Vertex.Semantic.Color.GetUnsafePtr(), SemanticIndex = 0u, Format = Vertex.Semantic.ALIGNED_FORMAT, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = D3D12_INPUT_CLASSIFICATION.D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, InstanceDataStepRate = 0 },
|
||||
];
|
||||
|
||||
public const DXGI_FORMAT SWAP_CHAIN_BACK_BUFFER_FORMAT = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
|
||||
public static D3D12_INPUT_LAYOUT_DESC InputLayoutDescription => new()
|
||||
{
|
||||
pInputElementDescs = (D3D12_INPUT_ELEMENT_DESC*)Unsafe.AsPointer(ref s_inputElementDescs[0]),
|
||||
NumElements = (uint)s_inputElementDescs.Length
|
||||
};
|
||||
}
|
||||
@@ -1,523 +0,0 @@
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.RHI;
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
using static TerraFX.Aliases.D3D12_Alias;
|
||||
using static TerraFX.Aliases.DXGI_Alias;
|
||||
|
||||
namespace Ghost.Graphics.D3D12.Utilities;
|
||||
|
||||
internal static unsafe class D3D12Utility
|
||||
{
|
||||
public static void SetName<T>(ref this T obj, ReadOnlySpan<char> name)
|
||||
where T : unmanaged, ID3D12Object.Interface
|
||||
{
|
||||
if (name.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (char* pName = name)
|
||||
{
|
||||
obj.SetName(pName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetName(ref this D3D12MA_Allocation obj, ReadOnlySpan<char> name)
|
||||
{
|
||||
if (name.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (char* pName = name)
|
||||
{
|
||||
obj.SetName(pName);
|
||||
}
|
||||
}
|
||||
|
||||
public static TextureDimension ToTextureDimension(this D3D12_RESOURCE_DIMENSION dimension)
|
||||
{
|
||||
return dimension switch
|
||||
{
|
||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE1D => TextureDimension.Texture2D,
|
||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE2D => TextureDimension.Texture2D,
|
||||
D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_TEXTURE3D => TextureDimension.Texture3D,
|
||||
_ => throw new NotSupportedException($"Resource dimension {dimension} is not supported."),
|
||||
};
|
||||
}
|
||||
|
||||
public static DXGI_FORMAT ToDXGIFormat(this TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.Unknown => DXGI_FORMAT_UNKNOWN,
|
||||
TextureFormat.R8G8B8A8_UNorm => DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
TextureFormat.B8G8R8A8_UNorm => DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
TextureFormat.R16G16B16A16_Float => DXGI_FORMAT_R16G16B16A16_FLOAT,
|
||||
TextureFormat.R32G32B32A32_Float => DXGI_FORMAT_R32G32B32A32_FLOAT,
|
||||
TextureFormat.D24_UNorm_S8_UInt => DXGI_FORMAT_D24_UNORM_S8_UINT,
|
||||
TextureFormat.D32_Float => DXGI_FORMAT_D32_FLOAT,
|
||||
_ => throw new NotSupportedException($"Texture format {format} is not supported."),
|
||||
};
|
||||
}
|
||||
|
||||
public static TextureFormat ToTextureFormat(this DXGI_FORMAT format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM => TextureFormat.R8G8B8A8_UNorm,
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM => TextureFormat.B8G8R8A8_UNorm,
|
||||
DXGI_FORMAT_R16G16B16A16_FLOAT => TextureFormat.R16G16B16A16_Float,
|
||||
DXGI_FORMAT_R32G32B32A32_FLOAT => TextureFormat.R32G32B32A32_Float,
|
||||
DXGI_FORMAT_D24_UNORM_S8_UINT => TextureFormat.D24_UNorm_S8_UInt,
|
||||
DXGI_FORMAT_D32_FLOAT => TextureFormat.D32_Float,
|
||||
_ => throw new NotSupportedException($"DXGI format {format} is not supported.")
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_RESOURCE_STATES ToD3D12States(this ResourceState state)
|
||||
{
|
||||
var d3dStates = D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COMMON;
|
||||
|
||||
if (state.HasFlag(ResourceState.VertexAndConstantBuffer))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.IndexBuffer))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_INDEX_BUFFER;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.RenderTarget))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.UnorderedAccess))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.DepthWrite))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.DepthRead))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_DEPTH_READ;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.PixelShaderResource))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.CopyDest))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.CopySource))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_COPY_SOURCE;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.GenericRead))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_GENERIC_READ;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.IndirectArgument))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
|
||||
}
|
||||
|
||||
if (state.HasFlag(ResourceState.NonPixelShaderResource))
|
||||
{
|
||||
d3dStates |= D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
|
||||
}
|
||||
|
||||
return d3dStates;
|
||||
}
|
||||
|
||||
public static ResourceState ToResourceState(this D3D12_RESOURCE_STATES states)
|
||||
{
|
||||
var resourceState = ResourceState.Common;
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER))
|
||||
{
|
||||
resourceState |= ResourceState.VertexAndConstantBuffer;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_INDEX_BUFFER))
|
||||
{
|
||||
resourceState |= ResourceState.IndexBuffer;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_RENDER_TARGET))
|
||||
{
|
||||
resourceState |= ResourceState.RenderTarget;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_UNORDERED_ACCESS))
|
||||
{
|
||||
resourceState |= ResourceState.UnorderedAccess;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_DEPTH_WRITE))
|
||||
{
|
||||
resourceState |= ResourceState.DepthWrite;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_DEPTH_READ))
|
||||
{
|
||||
resourceState |= ResourceState.DepthRead;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
resourceState |= ResourceState.PixelShaderResource;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_COPY_DEST))
|
||||
{
|
||||
resourceState |= ResourceState.CopyDest;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_COPY_SOURCE))
|
||||
{
|
||||
resourceState |= ResourceState.CopySource;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_GENERIC_READ))
|
||||
{
|
||||
resourceState |= ResourceState.GenericRead;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT))
|
||||
{
|
||||
resourceState |= ResourceState.IndirectArgument;
|
||||
}
|
||||
|
||||
if (states.HasFlag(D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE))
|
||||
{
|
||||
resourceState |= ResourceState.NonPixelShaderResource;
|
||||
}
|
||||
|
||||
return resourceState;
|
||||
}
|
||||
|
||||
public static D3D12_FILTER ToD3D12Filter(this TextureFilterMode filterMode)
|
||||
{
|
||||
return filterMode switch
|
||||
{
|
||||
TextureFilterMode.Point => D3D12_FILTER_MIN_MAG_MIP_POINT,
|
||||
TextureFilterMode.Bilinear => D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT,
|
||||
TextureFilterMode.Trilinear => D3D12_FILTER_MIN_MAG_MIP_LINEAR,
|
||||
TextureFilterMode.Anisotropic => D3D12_FILTER_ANISOTROPIC,
|
||||
_ => throw new ArgumentException($"Unknown texture filter mode: {filterMode}")
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_TEXTURE_ADDRESS_MODE ToD3D12TextureAddressMode(this TextureAddressMode addressMode)
|
||||
{
|
||||
return addressMode switch
|
||||
{
|
||||
TextureAddressMode.Repeat => D3D12_TEXTURE_ADDRESS_MODE_WRAP,
|
||||
TextureAddressMode.Mirror => D3D12_TEXTURE_ADDRESS_MODE_MIRROR,
|
||||
TextureAddressMode.Clamp => D3D12_TEXTURE_ADDRESS_MODE_CLAMP,
|
||||
TextureAddressMode.Border => D3D12_TEXTURE_ADDRESS_MODE_BORDER,
|
||||
TextureAddressMode.MirrorOnce => D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE,
|
||||
_ => throw new ArgumentException($"Unknown texture address mode: {addressMode}")
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_COMPARISON_FUNC ToD3D12ComparisonFunc(this ComparisonFunction func)
|
||||
{
|
||||
return func switch
|
||||
{
|
||||
ComparisonFunction.Never => D3D12_COMPARISON_FUNC_NEVER,
|
||||
ComparisonFunction.Less => D3D12_COMPARISON_FUNC_LESS,
|
||||
ComparisonFunction.Equal => D3D12_COMPARISON_FUNC_EQUAL,
|
||||
ComparisonFunction.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
||||
ComparisonFunction.Greater => D3D12_COMPARISON_FUNC_GREATER,
|
||||
ComparisonFunction.NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL,
|
||||
ComparisonFunction.GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL,
|
||||
ComparisonFunction.Always => D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
_ => throw new ArgumentException($"Unknown comparison function: {func}")
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_COMPARISON_FUNC ToD3DCompare(this ZTest z)
|
||||
{
|
||||
return z switch
|
||||
{
|
||||
ZTest.Disabled => D3D12_COMPARISON_FUNC_NEVER,
|
||||
ZTest.Less => D3D12_COMPARISON_FUNC_LESS,
|
||||
ZTest.LessEqual => D3D12_COMPARISON_FUNC_LESS_EQUAL,
|
||||
ZTest.Equal => D3D12_COMPARISON_FUNC_EQUAL,
|
||||
ZTest.GreaterEqual => D3D12_COMPARISON_FUNC_GREATER_EQUAL,
|
||||
ZTest.Greater => D3D12_COMPARISON_FUNC_GREATER,
|
||||
ZTest.NotEqual => D3D12_COMPARISON_FUNC_NOT_EQUAL,
|
||||
ZTest.Always => D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
_ => D3D12_COMPARISON_FUNC_LESS_EQUAL
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_COMMAND_LIST_TYPE ToCommandListType(CommandBufferType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
CommandBufferType.Graphics => D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
CommandBufferType.Compute => D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
||||
CommandBufferType.Copy => D3D12_COMMAND_LIST_TYPE_COPY,
|
||||
_ => throw new ArgumentException($"Unknown command buffer type: {type}")
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_RESOURCE_FLAGS ToD3D12ResourceFlag(this TextureUsage usage)
|
||||
{
|
||||
var flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
if (usage.HasFlag(TextureUsage.RenderTarget))
|
||||
{
|
||||
flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(TextureUsage.DepthStencil))
|
||||
{
|
||||
flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
if (usage.HasFlag(TextureUsage.UnorderedAccess))
|
||||
{
|
||||
flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
public static D3D12_RESOURCE_DESC ToD3D12ResourceDesc(this in TextureDesc desc)
|
||||
{
|
||||
var dxgiFormat = desc.Format.ToDXGIFormat();
|
||||
var maxDimension = Math.Max(desc.Width, Math.Max(desc.Height, desc.Slice));
|
||||
var mipLevels = desc.MipLevels == 0
|
||||
? (ushort)(1 + Math.Floor(Math.Log2(maxDimension)))
|
||||
: (ushort)desc.MipLevels;
|
||||
|
||||
var resourceFlags = desc.Usage.ToD3D12ResourceFlag();
|
||||
return desc.Dimension switch
|
||||
{
|
||||
TextureDimension.Texture2D => D3D12_RESOURCE_DESC.Tex2D(
|
||||
dxgiFormat,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: mipLevels,
|
||||
flags: resourceFlags),
|
||||
TextureDimension.Texture3D => D3D12_RESOURCE_DESC.Tex3D(
|
||||
dxgiFormat,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
(ushort)desc.Slice,
|
||||
flags: resourceFlags),
|
||||
TextureDimension.TextureCube => D3D12_RESOURCE_DESC.Tex2D(
|
||||
dxgiFormat,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: mipLevels,
|
||||
arraySize: 6,
|
||||
flags: resourceFlags),
|
||||
TextureDimension.Texture2DArray => D3D12_RESOURCE_DESC.Tex2D(
|
||||
dxgiFormat,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: mipLevels,
|
||||
arraySize: (ushort)desc.Slice,
|
||||
flags: resourceFlags),
|
||||
TextureDimension.TextureCubeArray => D3D12_RESOURCE_DESC.Tex2D(
|
||||
dxgiFormat,
|
||||
desc.Width,
|
||||
desc.Height,
|
||||
mipLevels: mipLevels,
|
||||
arraySize: (ushort)(desc.Slice * 6),
|
||||
flags: resourceFlags),
|
||||
_ => throw new ArgumentException($"Unsupported texture dimension: {desc.Dimension}"),
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_RESOURCE_FLAGS ToD3D12ResourceFlag(this BufferUsage usage)
|
||||
{
|
||||
var flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
if (usage.HasFlag(BufferUsage.Raw) || usage.HasFlag(BufferUsage.UnorderedAccess))
|
||||
{
|
||||
flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
public static D3D12_RESOURCE_DESC ToD3D12ResourceDesc(this in BufferDesc desc)
|
||||
{
|
||||
var alignedSize = desc.Size;
|
||||
if (desc.Usage.HasFlag(BufferUsage.Constant))
|
||||
{
|
||||
// D3D12 CBV size must be 256-byte aligned
|
||||
alignedSize = (uint)(desc.Size + 255) & ~255u;
|
||||
}
|
||||
|
||||
var resourceFlags = desc.Usage.ToD3D12ResourceFlag();
|
||||
return D3D12_RESOURCE_DESC.Buffer(alignedSize, resourceFlags);
|
||||
}
|
||||
|
||||
|
||||
public static ResourceDesc ToResourceDesc(this D3D12_RESOURCE_DESC desc)
|
||||
{
|
||||
if (desc.Dimension == D3D12_RESOURCE_DIMENSION.D3D12_RESOURCE_DIMENSION_BUFFER)
|
||||
{
|
||||
return ResourceDesc.Buffer(new BufferDesc
|
||||
{
|
||||
Size = (uint)desc.Width,
|
||||
Stride = 0,
|
||||
Usage = BufferUsage.None,
|
||||
MemoryType = ResourceMemoryType.Default
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResourceDesc.Texture(new TextureDesc
|
||||
{
|
||||
Width = (uint)desc.Width,
|
||||
Height = desc.Height,
|
||||
Slice = desc.DepthOrArraySize,
|
||||
Format = desc.Format.ToTextureFormat(),
|
||||
Dimension = desc.Dimension.ToTextureDimension(),
|
||||
MipLevels = desc.MipLevels,
|
||||
Usage = TextureUsage.None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_CREATE(
|
||||
D3D12_FILL_MODE fillMode,
|
||||
D3D12_CULL_MODE cullMode,
|
||||
bool frontCounterClockwise = false,
|
||||
int depthBias = D3D12_DEFAULT_DEPTH_BIAS,
|
||||
float depthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
|
||||
float slopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
|
||||
bool depthClipEnable = true,
|
||||
bool multisampleEnable = true,
|
||||
bool antialiasedLineEnable = false,
|
||||
uint forcedSampleCount = 0,
|
||||
D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF)
|
||||
{
|
||||
return new D3D12_RASTERIZER_DESC
|
||||
{
|
||||
FillMode = fillMode,
|
||||
CullMode = cullMode,
|
||||
FrontCounterClockwise = frontCounterClockwise ? TRUE : FALSE,
|
||||
DepthBias = depthBias,
|
||||
DepthBiasClamp = depthBiasClamp,
|
||||
SlopeScaledDepthBias = slopeScaledDepthBias,
|
||||
DepthClipEnable = depthClipEnable ? TRUE : FALSE,
|
||||
MultisampleEnable = multisampleEnable ? TRUE : FALSE,
|
||||
AntialiasedLineEnable = antialiasedLineEnable ? TRUE : FALSE,
|
||||
ForcedSampleCount = forcedSampleCount,
|
||||
ConservativeRaster = conservativeRaster
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_CULL_NONE => D3D12_RASTERIZER_DESC_CREATE(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE);
|
||||
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_CULL_CLOCKWISE => D3D12_RASTERIZER_DESC_CREATE(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_FRONT);
|
||||
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_CULL_COUNTER_CLOCKWISE => D3D12_RASTERIZER_DESC_CREATE(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_BACK);
|
||||
public static D3D12_RASTERIZER_DESC D3D12_RASTERIZER_DESC_WIREFRAME => D3D12_RASTERIZER_DESC_CREATE(D3D12_FILL_MODE_WIREFRAME, D3D12_CULL_MODE_NONE);
|
||||
|
||||
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_CREATE(D3D12_BLEND srcBlend, D3D12_BLEND destBlend)
|
||||
{
|
||||
var blendDesc = new D3D12_BLEND_DESC
|
||||
{
|
||||
AlphaToCoverageEnable = false,
|
||||
IndependentBlendEnable = false
|
||||
};
|
||||
|
||||
for (var i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
|
||||
{
|
||||
blendDesc.RenderTarget[i].BlendEnable = srcBlend != D3D12_BLEND_ONE || destBlend != D3D12_BLEND_ZERO;
|
||||
blendDesc.RenderTarget[i].LogicOp = D3D12_LOGIC_OP_NOOP;
|
||||
blendDesc.RenderTarget[i].SrcBlend = srcBlend;
|
||||
blendDesc.RenderTarget[i].DestBlend = destBlend;
|
||||
blendDesc.RenderTarget[i].BlendOp = D3D12_BLEND_OP_ADD;
|
||||
blendDesc.RenderTarget[i].SrcBlendAlpha = srcBlend;
|
||||
blendDesc.RenderTarget[i].DestBlendAlpha = destBlend;
|
||||
blendDesc.RenderTarget[i].BlendOpAlpha = D3D12_BLEND_OP_ADD;
|
||||
blendDesc.RenderTarget[i].RenderTargetWriteMask = (byte)D3D12_COLOR_WRITE_ENABLE_ALL;
|
||||
}
|
||||
|
||||
return blendDesc;
|
||||
}
|
||||
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_OPAQUE => D3D12_BLEND_DESC_CREATE(D3D12_BLEND_ONE, D3D12_BLEND_ZERO);
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_ALPHA_BLEND => D3D12_BLEND_DESC_CREATE(D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA);
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_ADDITIVE => D3D12_BLEND_DESC_CREATE(D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_ONE);
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_MULTIPLY => D3D12_BLEND_DESC_CREATE(D3D12_BLEND_DEST_COLOR, D3D12_BLEND_ZERO);
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_PREMULTIPLIED => D3D12_BLEND_DESC_CREATE(D3D12_BLEND_ONE, D3D12_BLEND_INV_SRC_ALPHA);
|
||||
public static D3D12_BLEND_DESC D3D12_BLEND_DESC_NON_PREMULTIPLIED => D3D12_BLEND_DESC_CREATE(D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA);
|
||||
|
||||
|
||||
public static D3D12_DEPTH_STENCIL_DESC D3D12_DEPTH_STENCIL_DESC_CREATE(
|
||||
bool depthEnable,
|
||||
bool depthWriteEnable,
|
||||
D3D12_COMPARISON_FUNC depthFunc,
|
||||
bool stencilEnable = false,
|
||||
byte stencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK,
|
||||
byte stencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK,
|
||||
D3D12_STENCIL_OP frontStencilFailOp = D3D12_STENCIL_OP_KEEP,
|
||||
D3D12_STENCIL_OP frontStencilDepthFailOp = D3D12_STENCIL_OP_KEEP,
|
||||
D3D12_STENCIL_OP frontStencilPassOp = D3D12_STENCIL_OP_KEEP,
|
||||
D3D12_COMPARISON_FUNC frontStencilFunc = D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
D3D12_STENCIL_OP backStencilFailOp = D3D12_STENCIL_OP_KEEP,
|
||||
D3D12_STENCIL_OP backStencilDepthFailOp = D3D12_STENCIL_OP_KEEP,
|
||||
D3D12_STENCIL_OP backStencilPassOp = D3D12_STENCIL_OP_KEEP,
|
||||
D3D12_COMPARISON_FUNC backStencilFunc = D3D12_COMPARISON_FUNC_ALWAYS)
|
||||
{
|
||||
return new D3D12_DEPTH_STENCIL_DESC
|
||||
{
|
||||
DepthEnable = depthEnable,
|
||||
DepthWriteMask = depthWriteEnable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO,
|
||||
DepthFunc = depthFunc,
|
||||
StencilEnable = stencilEnable,
|
||||
StencilReadMask = stencilReadMask,
|
||||
StencilWriteMask = stencilWriteMask,
|
||||
FrontFace = D3D12_DEPTH_STENCILOP_DESC_CREATE(frontStencilFailOp, frontStencilDepthFailOp, frontStencilPassOp, frontStencilFunc),
|
||||
BackFace = D3D12_DEPTH_STENCILOP_DESC_CREATE(backStencilFailOp, backStencilDepthFailOp, backStencilPassOp, backStencilFunc)
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_DEPTH_STENCIL_DESC D3D12_DEPTH_STENCIL_DESC_NONE => D3D12_DEPTH_STENCIL_DESC_CREATE(false, false, D3D12_COMPARISON_FUNC_LESS_EQUAL);
|
||||
public static D3D12_DEPTH_STENCIL_DESC D3D12_DEPTH_STENCIL_DESC_READ => D3D12_DEPTH_STENCIL_DESC_CREATE(true, false, D3D12_COMPARISON_FUNC_LESS_EQUAL);
|
||||
public static D3D12_DEPTH_STENCIL_DESC D3D12_DEPTH_STENCIL_DESC_REVERSE_Z => D3D12_DEPTH_STENCIL_DESC_CREATE(true, true, D3D12_COMPARISON_FUNC_GREATER_EQUAL);
|
||||
public static D3D12_DEPTH_STENCIL_DESC D3D12_DEPTH_STENCIL_DESC_READ_REVERSE_Z => D3D12_DEPTH_STENCIL_DESC_CREATE(true, false, D3D12_COMPARISON_FUNC_GREATER_EQUAL);
|
||||
|
||||
|
||||
public static D3D12_DEPTH_STENCILOP_DESC D3D12_DEPTH_STENCILOP_DESC_CREATE(
|
||||
D3D12_STENCIL_OP stencilFailOp,
|
||||
D3D12_STENCIL_OP stencilDepthFailOp,
|
||||
D3D12_STENCIL_OP stencilPassOp,
|
||||
D3D12_COMPARISON_FUNC stencilFunc)
|
||||
{
|
||||
return new D3D12_DEPTH_STENCILOP_DESC
|
||||
{
|
||||
StencilFailOp = stencilFailOp,
|
||||
StencilDepthFailOp = stencilDepthFailOp,
|
||||
StencilPassOp = stencilPassOp,
|
||||
StencilFunc = stencilFunc
|
||||
};
|
||||
}
|
||||
|
||||
public static D3D12_DEPTH_STENCILOP_DESC D3D12_DEPTH_STENCILOP_DESC_DEFAULT => D3D12_DEPTH_STENCILOP_DESC_CREATE(D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>embedded</DebugType>
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
@@ -16,27 +17,19 @@
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</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="Misaki.HighPerformance.Analyzer" Version="1.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Misaki.HighPerformance.Image" Version="1.1.0" />
|
||||
<PackageReference Include="TerraFX.Interop.D3D12MemoryAllocator" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../Runtime/Ghost.Core/Ghost.Core.csproj" />
|
||||
<ProjectReference Include="../../Editor/Ghost.DSL/Ghost.DSL.csproj" />
|
||||
<ProjectReference Include="..\..\Runtime\Ghost.Core\Ghost.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Editor\Ghost.DSL\Ghost.DSL.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Graphics.D3D12\Ghost.Graphics.D3D12.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Graphics.RHI\Ghost.Graphics.RHI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface ICommandAllocator : IDisposable
|
||||
{
|
||||
void Reset();
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
// TODO: Add ICommandAllocator support for thread local command buffers. We often use one allocator for multiple command buffers in a single frame.
|
||||
|
||||
/// <summary>
|
||||
/// D3D12-style command buffer interface for recording rendering commands
|
||||
/// </summary>
|
||||
public interface ICommandBuffer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the space of the command buffer.
|
||||
/// </summary>
|
||||
CommandBufferType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the command buffer contains any recorded commands.
|
||||
/// </summary>
|
||||
bool IsEmpty
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begins recording commands into this command buffer
|
||||
/// </summary>
|
||||
void Begin(ICommandAllocator allocator);
|
||||
|
||||
/// <summary>
|
||||
/// Ends recording commands and prepares for submission
|
||||
/// </summary>
|
||||
Result End();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the viewport for rendering
|
||||
/// </summary>
|
||||
/// <param name="viewport">Viewport to set</param>
|
||||
void SetViewport(ViewportDesc viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the scissor rectangle
|
||||
/// </summary>
|
||||
/// <param name="rect">Scissor rectangle to set</param>
|
||||
void SetScissorRect(RectDesc rect);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the optional render targets and optional depth Target for subsequent rendering operations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To specify no render targets, provide an empty span for <paramref name="renderTargets"/>.
|
||||
/// Use <see cref="Handle{Texture}.Invalid"/> for <paramref name="depthTarget"/> if no depth Target is required.
|
||||
/// </remarks>
|
||||
/// <param name="renderTargets">A read-only span of handles to textures that will be used as render targets.
|
||||
/// The order of handles determines the order in which render targets are bound.</param>
|
||||
/// <param name="depthTarget">A handle to the texture to be used as the depth Target. Specify a invalid handle if no depth Target is required.</param>
|
||||
void SetRenderTargets(ReadOnlySpan<Handle<Texture>> renderTargets, Handle<Texture> depthTarget);
|
||||
|
||||
void ClearRenderTargetView(Handle<Texture> renderTarget, Color128 clearColor);
|
||||
|
||||
void ClearDepthStencilView(Handle<Texture> depthStencil, bool inlcludeDepth, bool includeStencil, float clearDepth = 1.0f, byte clearStencil = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Begins a render pass with the specified render Target
|
||||
/// </summary>
|
||||
/// <param name="rtDescs">Render Target descriptions</param>
|
||||
/// <param name="depthDesc">Depth stencil description</param>
|
||||
/// <param name="allowUAVWrites">Whether UAV writes are allowed during the render pass</param>
|
||||
void BeginRenderPass(ReadOnlySpan<PassRenderTargetDesc> rtDescs, PassDepthStencilDesc depthDesc, bool allowUAVWrites = false);
|
||||
|
||||
/// <summary>
|
||||
/// Ends the current render pass
|
||||
/// </summary>
|
||||
void EndRenderPass();
|
||||
|
||||
// TODO: Enhanced barriers.
|
||||
|
||||
/// <summary>
|
||||
/// Inserts multiple resource barriers.
|
||||
/// </summary>
|
||||
/// <param name="barrierDescs">Resource barrier descriptions</param>
|
||||
void ResourceBarrier(params ReadOnlySpan<BarrierDesc> barrierDescs);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the pipeline state object
|
||||
/// </summary>
|
||||
/// <param name="pipelineKey">Pipeline state to set</param>
|
||||
void SetPipelineState(Key128<GraphicsPipeline> pipelineKey);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the constant buffer view for the specified slot in the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="slot">The zero-based index of the slot to bind the constant buffer view to.</param>
|
||||
/// <param name="buffer">A graphics buffer to use as the constant buffer view.</param>
|
||||
void SetConstantBufferView(uint slot, Handle<GraphicsBuffer> buffer);
|
||||
|
||||
/// <summary>
|
||||
/// Binds a vertex buffer to the specified slot for subsequent draw calls.
|
||||
/// </summary>
|
||||
/// <param name="slot">The vertex buffer slot to bind to.</param>
|
||||
/// <param name="buffer">The handle to the graphics buffer containing vertex data.</param>
|
||||
/// <param name="offset">The Offset in bytes from the start of the buffer.</param>
|
||||
void SetVertexBuffer(uint slot, Handle<GraphicsBuffer> buffer, ulong offset = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Binds an index buffer for indexed drawing.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The handle to the graphics buffer containing index data.</param>
|
||||
/// <param name="type">The space of indices (e.g., 16-bit or 32-bit).</param>
|
||||
/// <param name="offset">The Offset in bytes from the start of the buffer.</param>
|
||||
void SetIndexBuffer(Handle<GraphicsBuffer> buffer, IndexType type, ulong offset = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the primitive topology to be used for subsequent drawing operations.
|
||||
/// </summary>
|
||||
/// <param name="topology">The primitive topology that determines how the input vertices are interpreted during rendering.</param>
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a 32-bit constant value in the graphics root signature at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="rootIndex">The zero-based index of the root parameter in the graphics root signature to set the constant for.</param>
|
||||
/// <param name="constantBuffer">A read-only span containing the 32-bit constant values to set.</param>
|
||||
/// <param name="offsetIn32Bits">The Offset, in 32-bit values, from the start of the root parameter where the constants will be set.</param>
|
||||
void SetGraphicsRoot32Constants(uint rootIndex, ReadOnlySpan<uint> constantBuffer, uint offsetIn32Bits = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Issues a non-indexed draw call.
|
||||
/// </summary>
|
||||
/// <param name="vertexCount">Number of vertices to draw.</param>
|
||||
/// <param name="instanceCount">Number of instances to draw.</param>
|
||||
/// <param name="startVertex">Index of the first vertex to draw.</param>
|
||||
/// <param name="startInstance">Index of the first instance to draw.</param>
|
||||
void Draw(uint vertexCount, uint instanceCount = 1, uint startVertex = 0, uint startInstance = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Issues an indexed draw call.
|
||||
/// </summary>
|
||||
/// <param name="indexCount">Number of indices to draw.</param>
|
||||
/// <param name="instanceCount">Number of instances to draw.</param>
|
||||
/// <param name="startIndex">Index of the first index to draw.</param>
|
||||
/// <param name="baseVertex">Value added to each index before indexing the vertex buffer.</param>
|
||||
/// <param name="startInstance">Index of the first instance to draw.</param>
|
||||
void DrawIndexed(uint indexCount, uint instanceCount = 1, uint startIndex = 0, int baseVertex = 0, uint startInstance = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches compute threads
|
||||
/// </summary>
|
||||
/// <param name="threadGroupCountX">Thread groups in X dimension</param>
|
||||
/// <param name="threadGroupCountY">Thread groups in Y dimension</param>
|
||||
/// <param name="threadGroupCountZ">Thread groups in Z dimension</param>
|
||||
void DispatchCompute(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches mesh shader threads
|
||||
/// </summary>
|
||||
/// <param name="threadGroupCountX">Thread groups in X dimension</param>
|
||||
/// <param name="threadGroupCountY">Thread groups in Y dimension</param>
|
||||
/// <param name="threadGroupCountZ">Thread groups in Z dimension</param>
|
||||
void DispatchMesh(uint threadGroupCountX, uint threadGroupCountY, uint threadGroupCountZ);
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches ray tracing threads
|
||||
/// </summary>
|
||||
// TODO: This method is not supported yet.
|
||||
void DispatchRay();
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the specified data to the buffer represented by the given handle.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The unmanaged Value space of the elements to upload to the buffer.</typeparam>
|
||||
/// <param name="buffer">A handle to the buffer that will receive the uploaded data.</param>
|
||||
/// <param name="data">A read-only span containing the data to upload to the buffer. The span must contain elements of space
|
||||
/// <typeparamref name="T"/>.</param>
|
||||
void UploadBuffer<T>(Handle<GraphicsBuffer> buffer, params ReadOnlySpan<T> data)
|
||||
where T : unmanaged;
|
||||
|
||||
/// <summary>
|
||||
/// Uploads texture data to the specified texture resource starting at the given subresource index.
|
||||
/// </summary>
|
||||
/// <param name="texture">The texture resource to which the subresource data will be uploaded. Must be a valid, initialized texture handle.</param>
|
||||
/// <param name="subresources">A reference to the structure containing the subresource data to upload. The data must match the Format and layout expected by the texture.</param>
|
||||
/// Must be greater than zero and not exceed the remaining subresources in the texture.</param>
|
||||
void UploadTexture(Handle<Texture> texture, params ReadOnlySpan<SubResourceData> subresources);
|
||||
|
||||
/// <summary>
|
||||
/// Copies a specified number of bytes from the source graphics buffer to the destination graphics buffer.
|
||||
/// </summary>
|
||||
/// <param name="dest">The handle to the destination graphics buffer where data will be written.</param>
|
||||
/// <param name="src">The handle to the source graphics buffer from which data will be read.</param>
|
||||
/// <param name="destOffset">The byte Offset in the destination buffer at which to begin writing. Must be zero or greater.</param>
|
||||
/// <param name="srcOffset">The byte Offset in the source buffer at which to begin reading. Must be zero or greater.</param>
|
||||
/// <param name="numBytes">The number of bytes to copy. If zero, copies the remaining bytes from the source buffer starting at <paramref name="srcOffset"/>.</param>
|
||||
void CopyBuffer(Handle<GraphicsBuffer> dest, Handle<GraphicsBuffer> src, ulong destOffset = 0, ulong srcOffset = 0, ulong numBytes = 0);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
/// <summary>
|
||||
/// Command queue interface
|
||||
/// </summary>
|
||||
public interface ICommandQueue : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of commands this queue can execute
|
||||
/// </summary>
|
||||
public CommandQueueType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submits a single command buffer for execution
|
||||
/// </summary>
|
||||
/// <param name="commandBuffer">Command buffer to submit</param>
|
||||
public void Submit(ICommandBuffer commandBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Submits multiple command buffers for execution
|
||||
/// </summary>
|
||||
/// <param name="commandBuffers">Command buffers to submit</param>
|
||||
public void Submit(params ReadOnlySpan<ICommandBuffer> commandBuffers);
|
||||
|
||||
/// <summary>
|
||||
/// Signals a fence with the specified Value
|
||||
/// </summary>
|
||||
/// <param name="value">Value to signal</param>
|
||||
/// <returns>The fence Value that was signaled</returns>
|
||||
public ulong Signal(ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the fence to reach the specified Value
|
||||
/// </summary>
|
||||
/// <param name="value">Value to wait for</param>
|
||||
public void WaitForValue(ulong value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last completed fence Value
|
||||
/// </summary>
|
||||
/// <returns>Last completed fence Value</returns>
|
||||
public ulong GetCompletedValue();
|
||||
|
||||
/// <summary>
|
||||
/// Waits until all submitted commands have finished executing
|
||||
/// </summary>
|
||||
public void WaitIdle();
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IGraphicsEngine : IDisposable
|
||||
{
|
||||
IRenderDevice Device
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IShaderCompiler ShaderCompiler
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IPipelineLibrary PipelineLibrary
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IResourceDatabase ResourceDatabase
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IResourceAllocator ResourceAllocator
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of an object that implements the IRenderer interface.
|
||||
/// </summary>
|
||||
/// <returns>An object that provides rendering functionality through the IRenderer interface.</returns>
|
||||
IRenderer CreateRenderer();
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified renderer from the collection of active renderers.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The renderer instance to remove. Cannot be null.</param>
|
||||
void RemoveRenderer(IRenderer renderer);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all registered renderers from the collection.
|
||||
/// </summary>
|
||||
/// <remarks>Call this method to reset the renderer collection to an empty state. After calling this
|
||||
/// method, no renderers will be available until new ones are added.</remarks>
|
||||
void ClearRenderers();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new command allocator for the specified command buffer space.
|
||||
/// </summary>
|
||||
/// <param name="type">The space of command buffer for which to create the allocator. The default is CommandBufferType.Graphics.</param>
|
||||
/// <returns>An <see cref="ICommandAllocator"/> instance configured for the specified command buffer space.</returns>
|
||||
ICommandAllocator CreateCommandAllocator(CommandBufferType type = CommandBufferType.Graphics);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a command buffer for recording rendering commands
|
||||
/// </summary>
|
||||
/// <param name="type">Type of command buffer to create</param>
|
||||
/// <returns>A new command buffer instance</returns>
|
||||
ICommandBuffer CreateCommandBuffer(CommandBufferType type = CommandBufferType.Graphics);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a swap chain for presentation
|
||||
/// </summary>
|
||||
/// <param name="desc">Swap chain description</param>
|
||||
/// <returns>A new swap chain instance</returns>
|
||||
ISwapChain CreateSwapChain(SwapChainDesc desc);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the current frame.
|
||||
/// </summary>
|
||||
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||
/// <returns>Result of the rendering operation</returns>
|
||||
Result RenderFrame(ICommandAllocator commandAllocator);
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IPipelineLibrary : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Load pipeline library from disk.
|
||||
/// </summary>
|
||||
/// <param name="filePath">File path. If null, load default library.</param>
|
||||
void InitializeLibrary(string? filePath);
|
||||
void SaveLibraryToDisk(string filePath);
|
||||
bool HasPipeline(Key128<GraphicsPipeline> key);
|
||||
Result<Key128<GraphicsPipeline>> CompilePSO(ref readonly GraphicsPSODescriptor descriptor, ref readonly GraphicsCompiledResult compiled);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
[Flags]
|
||||
public enum FeatureSupport
|
||||
{
|
||||
None = 0,
|
||||
RayTracing = 1 << 0,
|
||||
VariableRateShading = 1 << 1,
|
||||
MeshShaders = 1 << 2,
|
||||
SamplerFeedback = 1 << 3,
|
||||
BindlessResources = 1 << 4,
|
||||
WorkGraphs = 1 << 5,
|
||||
AliasBuffersAndTextures = 1 << 6,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// D3D12-native render device interface for creating graphics resources
|
||||
/// </summary>
|
||||
public interface IRenderDevice : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Graphics command queue for rendering operations
|
||||
/// </summary>
|
||||
public ICommandQueue GraphicsQueue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute command queue for compute shader operations
|
||||
/// </summary>
|
||||
public ICommandQueue ComputeQueue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy command queue for data transfer operations
|
||||
/// </summary>
|
||||
public ICommandQueue CopyQueue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public FeatureSupport FeatureSupport
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
/// <summary>
|
||||
/// High-level renderer interface that uses RHI abstractions
|
||||
/// </summary>
|
||||
public interface IRenderer : IDisposable
|
||||
{
|
||||
IRenderOutput? RenderOutput
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders a frame
|
||||
/// </summary>
|
||||
/// <param name="commandAllocator">Command allocator to use for rendering</param>
|
||||
/// <returns>Result of the rendering operation</returns>
|
||||
Result Render(ICommandAllocator commandAllocator);
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.Core;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public enum ResourceAllocationType
|
||||
{
|
||||
Default,
|
||||
Temporary,
|
||||
Suballocation,
|
||||
}
|
||||
|
||||
public struct CreationOptions
|
||||
{
|
||||
public ResourceAllocationType AllocationType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Handle<GPUResource> Heap
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ulong Offset
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public enum HeapType
|
||||
{
|
||||
Default,
|
||||
Upload,
|
||||
Readback
|
||||
}
|
||||
|
||||
public enum HeapFlags
|
||||
{
|
||||
None = 0,
|
||||
AllowBuffers,
|
||||
AllowTextures,
|
||||
AllowRTAndDS,
|
||||
AlowBufferAndTexture,
|
||||
}
|
||||
|
||||
public struct AllocationDesc
|
||||
{
|
||||
public ulong Size
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ulong Alignment
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public HeapType HeapType
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public HeapFlags HeapFlags
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct ResourceSizeInfo
|
||||
{
|
||||
public ulong Size
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
|
||||
public ulong Alignment
|
||||
{
|
||||
get; init;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IResourceAllocator : IDisposable
|
||||
{
|
||||
ResourceSizeInfo GetSizeInfo(ResourceDesc desc);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a block of memory on the GPU
|
||||
/// </summary>
|
||||
/// <param name="desc">Allocation description</param>
|
||||
/// <param name="name">Debug name of the allocation</param>
|
||||
/// <returns>An <see cref="Handle{GPUResource}"/> point to the allocated memory</returns>
|
||||
Handle<GPUResource> Allocate(ref readonly AllocationDesc desc, string name);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Texture description</param>
|
||||
/// <param name="name">Debug name of the resource</param>
|
||||
/// <param name="options">Additional options of the resource allocation</param>
|
||||
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
|
||||
Handle<Texture> CreateTexture(ref readonly TextureDesc desc, string name, CreationOptions options = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a render Target for off-screen rendering
|
||||
/// </summary>
|
||||
/// <param name="desc">Render Target description</param>
|
||||
/// <param name="name">Debug name of the resource</param>
|
||||
/// <param name="options">Additional options of the resource allocation</param>
|
||||
/// <returns>An <see cref="Handle{Texture}"/> point to the resource</returns>
|
||||
Handle<Texture> CreateRenderTarget(ref readonly RenderTargetDesc desc, string name, CreationOptions options = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a buffer resource
|
||||
/// </summary>
|
||||
/// <param name="desc">Buffer description</param>
|
||||
/// <param name="name">Debug name of the resource</param>
|
||||
/// <param name="options">Additional options of the resource allocation</param>
|
||||
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> point to the resource</returns>
|
||||
Handle<GraphicsBuffer> CreateBuffer(ref readonly BufferDesc desc, string name, CreationOptions options = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary upload buffer of the specified size in bytes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method has been optimized for frequent calls during frame updates. It efficiently manages memory to minimize fragmentation and overhead.
|
||||
/// </remarks>
|
||||
/// <param name="sizeInBytes">The size of the upload buffer to create, in bytes.</param>
|
||||
/// <param name="offset">The offset within the upload buffer where the allocation begins.</param>
|
||||
/// <returns>An <see cref="Handle{GraphicsBuffer}"/> pointing to the created upload buffer.</returns>
|
||||
Handle<GraphicsBuffer> CreateTempUploadBuffer(ulong sizeInBytes, out ulong offset);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new sampler object using the specified sampler description.
|
||||
/// </summary>
|
||||
/// <param name="desc">A read-only reference to a <see cref="SamplerDesc"/> structure that defines the properties of the sampler to be created.</param>
|
||||
/// <returns>An <see cref="Identifier{Sampler}"/> that uniquely identifies the created sampler object.</returns>
|
||||
Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh from the specified vertex and index data.
|
||||
/// </summary>
|
||||
/// <param name="vertices">A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex.</param>
|
||||
/// <param name="indices">A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index.</param>
|
||||
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
|
||||
Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices);
|
||||
|
||||
/// <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. Cannot be null.</param>
|
||||
/// <returns>An <see cref="Identifier{Material}"/> representing the newly created material.</returns>
|
||||
Handle<Material> CreateMaterial(Identifier<Shader> shader);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader and returns its unique identifier.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor);
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
public interface IResourceReleasable
|
||||
{
|
||||
/// <summary>
|
||||
/// A method to release GPU resources.
|
||||
/// </summary>
|
||||
void ReleaseResource(IResourceDatabase database);
|
||||
}
|
||||
|
||||
public struct ResourceBarrierData
|
||||
{
|
||||
public BarrierLayout layout;
|
||||
public BarrierAccess access;
|
||||
public BarrierSync sync;
|
||||
|
||||
public ResourceBarrierData(BarrierLayout layout, BarrierAccess access, BarrierSync sync)
|
||||
{
|
||||
this.layout = layout;
|
||||
this.access = access;
|
||||
this.sync = sync;
|
||||
}
|
||||
}
|
||||
|
||||
public enum BindlessAccess
|
||||
{
|
||||
ShaderResource,
|
||||
ConstantBuffer,
|
||||
UnorderedAccess,
|
||||
}
|
||||
|
||||
// TODO: Consider adding methods for resource enumeration, statistics, and bulk operations.
|
||||
// TODO: Consider adding async resource loading and streaming support.
|
||||
// TODO: Mesh, Material, Shader management could be separated into their own interfaces for better modularity because they are not bound to specific graphics API.
|
||||
public interface IResourceDatabase : IDisposable
|
||||
{
|
||||
/*
|
||||
/// <summary>
|
||||
/// Imports an external unmanaged resource and returns a handle for use within the resource management system.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The space of the unmanaged resource pointer to import.</typeparam>
|
||||
/// <param name="resourcePtr">A pointer to the external unmanaged resource to be imported. Must remain valid for the duration of the resource's usage.</param>
|
||||
/// <param name="initialState">The initial state to assign to the imported resource.</param>
|
||||
/// <returns>A handle representing the imported resource, which can be used for subsequent operations.</returns>
|
||||
unsafe Handle<GPUResource> ImportExternalResource<T>(T resourcePtr, ResourceState initialState, string? name = null)
|
||||
where T : unmanaged;
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a resource with the specified handle exists in the database.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to check for existence.</param>
|
||||
bool HasResource(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the current barrier data of the specified resource.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle that uniquely identifies the resource.</param>
|
||||
/// <returns>A ResourceBarrierData value representing the current barrier state.</returns>
|
||||
Result<ResourceBarrierData, Error> GetResourceBarrierData(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the barrier data of the specified resource handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle that identifies the resource.</param>
|
||||
/// <param name="data">The new barrier data.</param>
|
||||
/// <returns>An Error indicating the success or failure of the operation.</returns>
|
||||
Error SetResourceBarrierData(Handle<GPUResource> handle, ResourceBarrierData data);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the description of a GPU resource associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">A handle that identifies the GPU resource for which to obtain the description. Must reference a valid resource.</param>
|
||||
/// <returns>A ResourceDesc structure containing details about the specified GPU resource.</returns>
|
||||
Result<ResourceDesc, Error> GetResourceDescription(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the bindless index associated with the specified GPU resource handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">A handle to the GPU resource for which to obtain the bindless index. Must reference a valid, currently registered resource.</param>
|
||||
/// <param name="access">The type of bindless access for which to obtain the index.</param>
|
||||
/// <returns>The bindless index corresponding to the specified GPU resource handle. ~0 if the resource does not support bindless access or is not found.</returns>
|
||||
uint GetBindlessIndex(Handle<GPUResource> handle, BindlessAccess access = BindlessAccess.ShaderResource);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the name of the GPU resource associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should only use this method in debug builds or inside engine editor.
|
||||
/// </remarks>
|
||||
/// <param name="handle">A handle to the GPU resource for which to obtain the name. Must reference a valid resource.</param>
|
||||
/// <returns>The name of the GPU resource associated with the specified handle, or null if the resource does not have a name.</returns>
|
||||
string? GetResourceName(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the GPU resource associated with the specified handle, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to be removed.</param>
|
||||
void ScheduleReleaseResource(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the GPU resource associated with the specified handle immediately, freeing any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the resource to be removed.</param>
|
||||
void ReleaseResourceImmediately(Handle<GPUResource> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an existing sampler identifier that matches the specified description, or creates a new one if none
|
||||
/// exists.
|
||||
/// </summary>
|
||||
/// <param name="desc">A read-only reference to a <see cref="SamplerDesc"/> structure that defines the properties of the sampler to retrieve or create.</param>
|
||||
/// <param name="id">An integer identifier to associate with the sampler.</param>
|
||||
/// <returns>An <see cref="Identifier{Sampler}"/> representing the sampler that matches the specified description.
|
||||
/// If a matching sampler does not exist, a new sampler is created and its identifier is returned.</returns>
|
||||
Identifier<Sampler> CreateSampler(ref readonly SamplerDesc desc, int id);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a sampler with the specified identifier exists.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the sampler to check for existence.</param>
|
||||
/// <returns>true if a sampler with the given identifier exists; otherwise, false.</returns>
|
||||
bool TryGetSampler(ref readonly SamplerDesc desc, out Identifier<Sampler> id);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the sampler associated with the specified identifier and frees any resources allocated to it.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the sampler to release. Must reference a valid, existing sampler.</param>
|
||||
void ReleaseSampler(Identifier<Sampler> id);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a mesh to the resource database and returns its handle.
|
||||
/// </summary>
|
||||
/// <param name="mesh">The mesh data to be added to the database.</param>
|
||||
/// <returns>The <see cref="Handle{Mesh}"/> representing the newly added mesh.</returns>"/>
|
||||
Handle<Mesh> AddMesh(ref readonly Mesh mesh);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a mesh with the specified Handle exists.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to check for existence. Cannot be null.</param>
|
||||
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
|
||||
bool HasMesh(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the mesh associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined.</param>
|
||||
/// <returns>A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
|
||||
void ReleaseMesh(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new material to the collection and returns its unique handle.
|
||||
/// </summary>
|
||||
/// <param name="material">The material to add. The material must be fully initialized before calling this method.</param>
|
||||
/// <returns>The <see cref="Handle{Material}"/> representing the newly added material.</returns>
|
||||
Handle<Material> AddMaterial(ref readonly Material material);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a material with the specified handle exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to check for existence.</param>
|
||||
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
|
||||
bool HasMaterial(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the material associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to retrieve. Must refer to a valid material.</param>
|
||||
/// <returns>A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
RefResult<Material, Error> GetMaterialReference(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the material associated with the specified handle, making it available for reuse or disposal.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
|
||||
void ReleaseMaterial(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified shader to the collection and returns its unique identifier.
|
||||
/// </summary>
|
||||
/// <param name="shader">The shader to add. The shader is passed by read-only reference and will not be modified.</param>
|
||||
/// <returns>The <see cref="Identifier{Shader}"/> representing the newly added shader.</returns>
|
||||
Identifier<Shader> AddShader(Shader shader);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a shader with the specified identifier exists in the collection.
|
||||
/// </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>
|
||||
bool HasShader(Identifier<Shader> id);
|
||||
|
||||
/// <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>
|
||||
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
|
||||
RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id);
|
||||
|
||||
/// <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>
|
||||
void ReleaseShader(Identifier<Shader> id);
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
/// <summary>
|
||||
/// Swap chain interface for presentation
|
||||
/// </summary>
|
||||
public interface ISwapChain : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Width of the swap chain back buffers
|
||||
/// </summary>
|
||||
uint Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Height of the swap chain back buffers
|
||||
/// </summary>
|
||||
uint Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the horizontal scaling factor applied to the object. This is used for DPI scaling.
|
||||
/// </summary>
|
||||
float ScaleX
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vertical scale factor applied to the object. This is used for DPI scaling.
|
||||
/// </summary>
|
||||
float ScaleY
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current back buffer texture
|
||||
/// </summary>
|
||||
/// <returns>Current back buffer texture</returns>
|
||||
Handle<Texture> GetCurrentBackBuffer();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all back buffer textures
|
||||
/// </summary>
|
||||
/// <returns>AlowBufferAndTexture back buffer textures</returns>
|
||||
ReadOnlySpan<Handle<Texture>> GetBackBuffers();
|
||||
|
||||
/// <summary>
|
||||
/// Presents the rendered frame
|
||||
/// </summary>
|
||||
/// <param name="vsync">Enable vertical synchronization</param>
|
||||
void Present(bool vsync = true);
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the swap chain back buffers
|
||||
/// </summary>
|
||||
/// <param name="width">New Width</param>
|
||||
/// <param name="height">New Height</param>
|
||||
void Resize(uint width, uint height);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the horizontal and vertical scaling factors for the object.
|
||||
/// </summary>
|
||||
/// <param name="scaleX">The factor by which to scale the object along the X-axis.</param>
|
||||
/// <param name="scaleY">The factor by which to scale the object along the Y-axis.</param>
|
||||
void SetScale(float scaleX, float scaleY);
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Core.Utilities;
|
||||
using Ghost.Graphics.Core;
|
||||
using System.IO.Hashing;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ghost.Graphics.RHI;
|
||||
|
||||
internal static class RHIUtility
|
||||
{
|
||||
public const int MAX_RENDER_TARGETS = 8;
|
||||
|
||||
public static uint GetBytesPerPixel(this TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.R8G8B8A8_UNorm => 4,
|
||||
TextureFormat.B8G8R8A8_UNorm => 4,
|
||||
TextureFormat.R16G16B16A16_Float => 8,
|
||||
TextureFormat.R32G32B32A32_Float => 16,
|
||||
TextureFormat.D24_UNorm_S8_UInt => 4,
|
||||
TextureFormat.D32_Float => 4,
|
||||
_ => throw new NotSupportedException($"Texture format {format} is not supported."),
|
||||
};
|
||||
}
|
||||
|
||||
public static uint GetTotalBytes(this TextureDesc desc)
|
||||
{
|
||||
return desc.Format.GetBytesPerPixel() * desc.Width * desc.Height * desc.Slice;
|
||||
}
|
||||
|
||||
public static void GetSurfaceInfo(this TextureFormat format, uint width, uint height, out uint rowPitch, out uint slicePitch, out uint rowCount)
|
||||
{
|
||||
var bc = false;
|
||||
var packed = false;
|
||||
var planar = false;
|
||||
var bpe = 0u;
|
||||
|
||||
//switch (Format)
|
||||
//{
|
||||
// case Format.BC1Typeless:
|
||||
// case Format.BC1Unorm:
|
||||
// case Format.BC1UnormSrgb:
|
||||
// case Format.BC4Typeless:
|
||||
// case Format.BC4Unorm:
|
||||
// case Format.BC4Snorm:
|
||||
// bc = true;
|
||||
// bpe = 8;
|
||||
// break;
|
||||
|
||||
// case Format.BC2Typeless:
|
||||
// case Format.BC2Unorm:
|
||||
// case Format.BC2UnormSrgb:
|
||||
// case Format.BC3Typeless:
|
||||
// case Format.BC3Unorm:
|
||||
// case Format.BC3UnormSrgb:
|
||||
// case Format.BC5Typeless:
|
||||
// case Format.BC5Unorm:
|
||||
// case Format.BC5Snorm:
|
||||
// case Format.BC6HTypeless:
|
||||
// case Format.BC6HUF16:
|
||||
// case Format.BC6HSF16:
|
||||
// case Format.BC7Typeless:
|
||||
// case Format.BC7Unorm:
|
||||
// case Format.BC7UnormSrgb:
|
||||
// bc = true;
|
||||
// bpe = 16;
|
||||
// break;
|
||||
|
||||
// case Format.R8G8_B8G8Unorm:
|
||||
// case Format.G8R8_G8B8Unorm:
|
||||
// case Format.YUY2:
|
||||
// packed = true;
|
||||
// bpe = 4;
|
||||
// break;
|
||||
|
||||
// case Format.Y210:
|
||||
// case Format.Y216:
|
||||
// packed = true;
|
||||
// bpe = 8;
|
||||
// break;
|
||||
|
||||
// case Format.NV12:
|
||||
// case Format.Opaque420:
|
||||
// case Format.P208:
|
||||
// planar = true;
|
||||
// bpe = 2;
|
||||
// break;
|
||||
|
||||
// case Format.P010:
|
||||
// case Format.P016:
|
||||
// planar = true;
|
||||
// bpe = 4;
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// break;
|
||||
//}
|
||||
|
||||
if (bc)
|
||||
{
|
||||
var numBlocksWide = 0u;
|
||||
if (width > 0)
|
||||
{
|
||||
numBlocksWide = Math.Max(1u, (width + 3) / 4u);
|
||||
}
|
||||
|
||||
var numBlocksHigh = 0u;
|
||||
if (height > 0)
|
||||
{
|
||||
numBlocksHigh = Math.Max(1u, (height + 3) / 4u);
|
||||
}
|
||||
|
||||
rowPitch = numBlocksWide * bpe;
|
||||
rowCount = numBlocksHigh;
|
||||
slicePitch = rowPitch * numBlocksHigh;
|
||||
}
|
||||
else if (packed)
|
||||
{
|
||||
rowPitch = ((width + 1u) >> 1) * bpe;
|
||||
rowCount = height;
|
||||
slicePitch = rowPitch * height;
|
||||
}
|
||||
else if (planar)
|
||||
{
|
||||
rowPitch = ((width + 1u) >> 1) * bpe;
|
||||
slicePitch = (rowPitch * height) + ((rowPitch * height + 1) >> 1);
|
||||
rowCount = height + ((height + 1u) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
var bpp = GetBytesPerPixel(format) * 8;
|
||||
rowPitch = (width * bpp + 7) / 8; // round up to nearest byte
|
||||
rowCount = height;
|
||||
slicePitch = rowPitch * height;
|
||||
}
|
||||
}
|
||||
|
||||
public static Key64<ShaderPass> CreateShaderPassKey(string passID)
|
||||
{
|
||||
var passIdSpan = passID.AsSpan();
|
||||
return new Key64<ShaderPass>(XxHash3.HashToUInt64(MemoryMarshal.AsBytes(passIdSpan)));
|
||||
}
|
||||
|
||||
public static Key64<ShaderVariant> CreateShaderVariantKey(Key64<ShaderPass> passKey, ref readonly LocalKeywordSet keywords)
|
||||
{
|
||||
var passHash = passKey.Value;
|
||||
var keywordHash = keywords.GetHash64();
|
||||
return new Key64<ShaderVariant>(Hash.Hash64(passHash, keywordHash));
|
||||
}
|
||||
|
||||
public static unsafe Key128<GraphicsPipeline> CreateGraphicsPipelineKey(Key64<ShaderVariant> shaderVariantKey, PipelineState pipelineState, PassPipelineHash 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;
|
||||
}
|
||||
|
||||
var mLo = shaderVariantKey.Value;
|
||||
var mHi = pipelineState.GetHashCode64();
|
||||
|
||||
var pPasskey = (ulong*)&passKey.value;
|
||||
var pLo = pPasskey[0];
|
||||
var pHi = pPasskey[1];
|
||||
|
||||
// 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 Key128<GraphicsPipeline>(new UInt128(lo, hi));
|
||||
}
|
||||
|
||||
public static bool TryGetString(this Key128<GraphicsPipeline> key, Span<char> destination)
|
||||
{
|
||||
return key.Value.TryFormat(destination, out var _, "X16");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
@@ -10,7 +9,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
/// </summary>
|
||||
public sealed class RenderGraph : IDisposable
|
||||
{
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
|
||||
private readonly RenderGraphObjectPool _objectPool;
|
||||
private readonly RenderGraphResourceRegistry _resources;
|
||||
@@ -31,16 +30,15 @@ public sealed class RenderGraph : IDisposable
|
||||
private readonly RenderGraphExecutor _executor;
|
||||
private readonly RenderGraphNativePassBuilder _nativePassBuilder;
|
||||
|
||||
private readonly RenderGraphBlackboard _blackboard;
|
||||
|
||||
private bool _compiled;
|
||||
|
||||
public RenderGraphBlackboard Blackboard
|
||||
{
|
||||
get;
|
||||
}
|
||||
public RenderGraphBlackboard Blackboard => _blackboard;
|
||||
|
||||
public RenderGraph(IGraphicsEngine graphicsEngine)
|
||||
public RenderGraph(IResourceManager resourceManager, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler)
|
||||
{
|
||||
_graphicsEngine = graphicsEngine;
|
||||
_resourceManager = resourceManager;
|
||||
|
||||
_objectPool = new RenderGraphObjectPool();
|
||||
_resources = new RenderGraphResourceRegistry(_objectPool);
|
||||
@@ -50,40 +48,32 @@ public sealed class RenderGraph : IDisposable
|
||||
_nativePasses = new List<NativeRenderPass>(32);
|
||||
|
||||
_builder = new RenderGraphBuilder();
|
||||
_aliasingManager = new ResourceAliasingManager(graphicsEngine.ResourceAllocator, _objectPool);
|
||||
_aliasingManager = new ResourceAliasingManager(resourceManager.ResourceAllocator, _objectPool);
|
||||
|
||||
_compilationCache = new RenderGraphCompilationCache();
|
||||
|
||||
_context = new RenderGraphContext(
|
||||
_graphicsEngine.ResourceDatabase,
|
||||
_graphicsEngine.PipelineLibrary,
|
||||
_graphicsEngine.ShaderCompiler,
|
||||
resourceManager,
|
||||
pipelineLibrary,
|
||||
shaderCompiler,
|
||||
_resources
|
||||
);
|
||||
|
||||
_nativePassBuilder = new RenderGraphNativePassBuilder(_objectPool, _resources);
|
||||
_compiler = new RenderGraphCompiler(_graphicsEngine, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
|
||||
_executor = new RenderGraphExecutor(_graphicsEngine, _resources, _context);
|
||||
_compiler = new RenderGraphCompiler(resourceManager, _resources, _aliasingManager, _nativePassBuilder, _compilationCache);
|
||||
_executor = new RenderGraphExecutor(resourceManager, _resources, _context);
|
||||
|
||||
Blackboard = new RenderGraphBlackboard();
|
||||
_blackboard = new RenderGraphBlackboard();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the render graph for a new frame.
|
||||
/// Reuses existing allocations to minimize GC.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
// Clear blackboard data
|
||||
Blackboard.Clear();
|
||||
|
||||
// Reset resources but keep allocations
|
||||
_resources.Reset();
|
||||
|
||||
// Reset aliasing manager
|
||||
_aliasingManager.Reset();
|
||||
|
||||
// Clear compiled barriers
|
||||
_blackboard.Clear();
|
||||
_resources.Clear();
|
||||
_aliasingManager.Clear();
|
||||
_compiledBarriers.Clear();
|
||||
|
||||
// Return passes to the pool and reset count
|
||||
@@ -94,11 +84,8 @@ public sealed class RenderGraph : IDisposable
|
||||
}
|
||||
|
||||
_passes.Clear();
|
||||
|
||||
// Clear compiled passes list
|
||||
_compiledPasses.Clear();
|
||||
|
||||
// Return native passes to pool
|
||||
for (var i = 0; i < _nativePasses.Count; i++)
|
||||
{
|
||||
_objectPool.Return(_nativePasses[i]);
|
||||
@@ -117,14 +104,14 @@ public sealed class RenderGraph : IDisposable
|
||||
Color128 clearColor = default, float clearDepth = 1.0f, byte clearStencil = 0,
|
||||
bool clearAtFirstUse = true, bool discardAtLastUse = true)
|
||||
{
|
||||
var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(texture.AsResource());
|
||||
var r = _resourceManager.ResourceDatabase.GetResourceDescription(texture.AsResource());
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return Identifier<RGTexture>.Invalid;
|
||||
}
|
||||
|
||||
var desc = r.Value;
|
||||
return _resources.ImportTexture(in desc._desc.textureDescription, texture, name, clearColor, clearDepth, clearStencil, clearAtFirstUse, discardAtLastUse);
|
||||
return _resources.ImportTexture(in desc.TextureDescription, texture, name, clearColor, clearDepth, clearStencil, clearAtFirstUse, discardAtLastUse);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -134,14 +121,14 @@ public sealed class RenderGraph : IDisposable
|
||||
/// <returns>The identifier of the imported render graph buffer. Invalid if import fails.</returns>
|
||||
public Identifier<RGBuffer> ImportBuffer(Handle<GraphicsBuffer> buffer, string name)
|
||||
{
|
||||
var r = _graphicsEngine.ResourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||
var r = _resourceManager.ResourceDatabase.GetResourceDescription(buffer.AsResource());
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return Identifier<RGBuffer>.Invalid;
|
||||
}
|
||||
|
||||
var desc = r.Value;
|
||||
return _resources.ImportBuffer(in desc._desc.bufferDescription, buffer, name);
|
||||
return _resources.ImportBuffer(in desc.BufferDescription, buffer, name);
|
||||
}
|
||||
|
||||
public IRasterRenderGraphBuilder AddRasterRenderPass<TPassData>(string name, out TPassData passData)
|
||||
@@ -199,7 +186,7 @@ public sealed class RenderGraph : IDisposable
|
||||
|
||||
// Compute structural hash for caching
|
||||
var graphHash = RenderGraphHasher.ComputeGraphHash(_passes, _resources);
|
||||
|
||||
|
||||
// Delegate to compiler
|
||||
_compiler.Compile(in viewState, graphHash, _passes, _compiledPasses, _nativePasses, _compiledBarriers);
|
||||
_compiled = true;
|
||||
|
||||
@@ -326,7 +326,7 @@ internal sealed class ResourceAliasingManager
|
||||
_logicalToPlaced = new Dictionary<int, int>(64);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
public void Clear()
|
||||
{
|
||||
for (var i = 0; i < _placedResources.Count; i++)
|
||||
{
|
||||
|
||||
@@ -74,7 +74,7 @@ internal sealed class ResourceStateTracker
|
||||
|
||||
/// <summary>
|
||||
/// Represents a compiled barrier with only the target state.
|
||||
/// The before state is always queried from ResourceDatabase at execution time.
|
||||
/// The before state is always queried from ResourceManager at execution time.
|
||||
/// </summary>
|
||||
internal struct CompiledBarrier
|
||||
{
|
||||
@@ -208,7 +208,7 @@ internal static class RenderGraphBarriers
|
||||
|
||||
/// <summary>
|
||||
/// Compiles implicit state transitions for all resources accessed by a pass.
|
||||
/// Stores only the target state - the before state will be queried from ResourceDatabase at execution time.
|
||||
/// Stores only the target state - the before state will be queried from ResourceManager at execution time.
|
||||
/// </summary>
|
||||
private static void CompileImplicitTransitions(
|
||||
RenderGraphPassBase pass,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
@@ -23,7 +22,7 @@ internal sealed class CachedCompilation
|
||||
// Placed resource metadata
|
||||
public readonly List<PlacedResourceData> placedResources = new(32);
|
||||
|
||||
// Compiled barriers (stores only target states, queries before state from ResourceDatabase)
|
||||
// Compiled barriers (stores only target states, queries before state from ResourceManager)
|
||||
public readonly List<CompiledBarrier> compiledBarriers = new(128);
|
||||
|
||||
// Real gpu resource
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
@@ -10,7 +9,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
/// </summary>
|
||||
internal sealed class RenderGraphCompiler
|
||||
{
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly RenderGraphResourceRegistry _resources;
|
||||
private readonly ResourceAliasingManager _aliasingManager;
|
||||
private readonly RenderGraphNativePassBuilder _nativePassBuilder;
|
||||
@@ -19,13 +18,13 @@ internal sealed class RenderGraphCompiler
|
||||
private Handle<GPUResource> _resourceHeap;
|
||||
|
||||
public RenderGraphCompiler(
|
||||
IGraphicsEngine graphicsEngine,
|
||||
IResourceManager resourceManager,
|
||||
RenderGraphResourceRegistry resources,
|
||||
ResourceAliasingManager aliasingManager,
|
||||
RenderGraphNativePassBuilder nativePassBuilder,
|
||||
RenderGraphCompilationCache compilationCache)
|
||||
{
|
||||
_graphicsEngine = graphicsEngine;
|
||||
_resourceManager = resourceManager;
|
||||
_resources = resources;
|
||||
_aliasingManager = aliasingManager;
|
||||
_nativePassBuilder = nativePassBuilder;
|
||||
@@ -212,10 +211,10 @@ internal sealed class RenderGraphCompiler
|
||||
continue;
|
||||
}
|
||||
|
||||
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
|
||||
}
|
||||
|
||||
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
|
||||
}
|
||||
|
||||
if (_aliasingManager.Heap.size == 0)
|
||||
@@ -231,7 +230,7 @@ internal sealed class RenderGraphCompiler
|
||||
HeapType = HeapType.Default
|
||||
};
|
||||
|
||||
_resourceHeap = _graphicsEngine.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
|
||||
_resourceHeap = _resourceManager.ResourceAllocator.Allocate(in allocationDesc, "RenderGraphResourceHeap");
|
||||
|
||||
for (var i = 0; i < _resources.Resources.Count; i++)
|
||||
{
|
||||
@@ -253,11 +252,11 @@ internal sealed class RenderGraphCompiler
|
||||
if (res.type == RenderGraphResourceType.Texture)
|
||||
{
|
||||
var textureDesc = res.rgTextureDesc.ToTextureDesc(res.resolvedWidth, res.resolvedHeight);
|
||||
res.backingResource = _graphicsEngine.ResourceAllocator.CreateTexture(in textureDesc, res.name, ops).AsResource();
|
||||
res.backingResource = _resourceManager.ResourceAllocator.CreateTexture(in textureDesc, res.name, ops).AsResource();
|
||||
}
|
||||
else if (res.type == RenderGraphResourceType.Buffer)
|
||||
{
|
||||
res.backingResource = _graphicsEngine.ResourceAllocator.CreateBuffer(in res.bufferDesc, res.name, ops).AsResource();
|
||||
res.backingResource = _resourceManager.ResourceAllocator.CreateBuffer(in res.bufferDesc, res.name, ops).AsResource();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -380,11 +379,11 @@ internal sealed class RenderGraphCompiler
|
||||
{
|
||||
if (!res.isImported)
|
||||
{
|
||||
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(res.backingResource);
|
||||
}
|
||||
}
|
||||
|
||||
_graphicsEngine.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
|
||||
_resourceManager.ResourceDatabase.ScheduleReleaseResource(_resourceHeap);
|
||||
_resourceHeap = Handle<GPUResource>.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
@@ -8,7 +7,7 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
|
||||
public interface IRenderGraphContext
|
||||
{
|
||||
IResourceDatabase ResourceDatabase { get; }
|
||||
IResourceManager ResourceManager { get; }
|
||||
|
||||
Handle<GPUResource> GetActualResource(Identifier<RGResource> resource);
|
||||
Handle<Texture> GetActualTexture(Identifier<RGTexture> texture);
|
||||
@@ -38,7 +37,7 @@ public interface IUnsafeRenderContext : IRasterRenderContext, IRenderGraphContex
|
||||
|
||||
internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderContext, IUnsafeRenderContext
|
||||
{
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly IPipelineLibrary _pipelineLibrary;
|
||||
private readonly IShaderCompiler _shaderCompiler;
|
||||
private readonly RenderGraphResourceRegistry _resources;
|
||||
@@ -53,15 +52,15 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
private Handle<GraphicsBuffer> _activePerMeshData;
|
||||
private int _activeMeshIndexCount;
|
||||
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
public IResourceManager ResourceManager => _resourceManager;
|
||||
|
||||
public int ActiveMeshIndexCount => _activeMeshIndexCount;
|
||||
|
||||
public ICommandBuffer CommandBuffer => _commandBuffer;
|
||||
|
||||
internal RenderGraphContext(IResourceDatabase resourceDatabase, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
||||
internal RenderGraphContext(IResourceManager resourceManager, IPipelineLibrary pipelineLibrary, IShaderCompiler shaderCompiler, RenderGraphResourceRegistry resources)
|
||||
{
|
||||
_resourceDatabase = resourceDatabase;
|
||||
_resourceManager = resourceManager;
|
||||
_pipelineLibrary = pipelineLibrary;
|
||||
_shaderCompiler = shaderCompiler;
|
||||
_resources = resources;
|
||||
@@ -103,7 +102,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
public void SetActiveMaterial(Handle<Material> material)
|
||||
{
|
||||
var r = _resourceDatabase.GetMaterialReference(material);
|
||||
var r = _resourceManager.GetMaterialReference(material);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
_activePerMaterialData = Handle<GraphicsBuffer>.Invalid;
|
||||
@@ -116,7 +115,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
public void SetActiveMaterial(ref readonly Material material)
|
||||
{
|
||||
var shaderResult = _resourceDatabase.GetShaderReference(material.Shader);
|
||||
var shaderResult = _resourceManager.GetShaderReference(material.Shader);
|
||||
if (shaderResult.IsFailure)
|
||||
{
|
||||
_activePerMaterialData = Handle<GraphicsBuffer>.Invalid;
|
||||
@@ -161,7 +160,7 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
|
||||
public void SetActiveMesh(Handle<Mesh> mesh)
|
||||
{
|
||||
var r = _resourceDatabase.GetMeshReference(mesh);
|
||||
var r = _resourceManager.GetMeshReference(mesh);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
_activePerMeshData = Handle<GraphicsBuffer>.Invalid;
|
||||
@@ -184,8 +183,8 @@ internal sealed class RenderGraphContext : IRasterRenderContext, IComputeRenderC
|
||||
// TODO: Global and view constants
|
||||
var data = new PushConstantsData
|
||||
{
|
||||
objectIndex = _resourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
|
||||
materialIndex = _resourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
|
||||
objectIndex = _resourceManager.ResourceDatabase.GetBindlessIndex(_activePerMeshData.AsResource()),
|
||||
materialIndex = _resourceManager.ResourceDatabase.GetBindlessIndex(_activePerMaterialData.AsResource()),
|
||||
};
|
||||
|
||||
var pushConstantSpan = new ReadOnlySpan<uint>(&data, sizeof(PushConstantsData) / sizeof(uint));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
|
||||
namespace Ghost.Graphics.RenderGraphModule;
|
||||
@@ -9,16 +8,16 @@ namespace Ghost.Graphics.RenderGraphModule;
|
||||
/// </summary>
|
||||
internal sealed class RenderGraphExecutor
|
||||
{
|
||||
private readonly IGraphicsEngine _graphicsEngine;
|
||||
private readonly IResourceManager _resourceManager;
|
||||
private readonly RenderGraphResourceRegistry _resources;
|
||||
private readonly RenderGraphContext _context;
|
||||
|
||||
public RenderGraphExecutor(
|
||||
IGraphicsEngine graphicsEngine,
|
||||
IResourceManager resourceManager,
|
||||
RenderGraphResourceRegistry resources,
|
||||
RenderGraphContext context)
|
||||
{
|
||||
_graphicsEngine = graphicsEngine;
|
||||
_resourceManager = resourceManager;
|
||||
_resources = resources;
|
||||
_context = context;
|
||||
}
|
||||
@@ -130,7 +129,7 @@ internal sealed class RenderGraphExecutor
|
||||
|
||||
/// <summary>
|
||||
/// Executes all barriers for a specific pass.
|
||||
/// Uses pre-compiled barriers and queries before state from ResourceDatabase.
|
||||
/// Uses pre-compiled barriers and queries before state from ResourceManager.
|
||||
/// </summary>
|
||||
private unsafe void ExecuteBarriersForPass(
|
||||
ICommandBuffer cmd,
|
||||
@@ -158,8 +157,8 @@ internal sealed class RenderGraphExecutor
|
||||
var resource = _resources.GetResource(compiledBarrier.Resource);
|
||||
var resourceHandle = resource.backingResource;
|
||||
|
||||
// Always query the before state from ResourceDatabase (single source of truth)
|
||||
var currentState = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow();
|
||||
// Always query the before state from ResourceManager (single source of truth)
|
||||
var currentState = _resourceManager.ResourceDatabase.GetResourceBarrierData(resourceHandle).GetValueOrThrow();
|
||||
|
||||
BarrierLayout layoutBefore;
|
||||
BarrierAccess accessBefore;
|
||||
@@ -169,7 +168,7 @@ internal sealed class RenderGraphExecutor
|
||||
if (compiledBarrier.AliasingPredecessor.IsValid)
|
||||
{
|
||||
var predHandle = _resources.GetResource(compiledBarrier.AliasingPredecessor).backingResource;
|
||||
var predState = _graphicsEngine.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow();
|
||||
var predState = _resourceManager.ResourceDatabase.GetResourceBarrierData(predHandle).GetValueOrThrow();
|
||||
|
||||
layoutBefore = BarrierLayout.Undefined;
|
||||
accessBefore = BarrierAccess.NoAccess;
|
||||
|
||||
@@ -168,7 +168,7 @@ internal sealed class RenderGraphResourceRegistry
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
public void Clear()
|
||||
{
|
||||
// Return all resources to pool
|
||||
for (var i = 0; i < _resources.Count; i++)
|
||||
|
||||
@@ -36,16 +36,23 @@ public struct ViewState : IEquatable<ViewState>
|
||||
{
|
||||
public uint viewportWidth;
|
||||
public uint viewportHeight;
|
||||
|
||||
// For upscalers that need to know the original render target size before upscaling
|
||||
public uint actualWidth;
|
||||
public uint actualHeight;
|
||||
|
||||
public ViewState(uint width, uint height)
|
||||
public ViewState(uint width, uint height, uint actualWidth, uint actualHeight)
|
||||
{
|
||||
viewportWidth = width;
|
||||
viewportHeight = height;
|
||||
this.actualWidth = actualWidth;
|
||||
this.actualHeight = actualHeight;
|
||||
}
|
||||
|
||||
public readonly bool Equals(ViewState other)
|
||||
{
|
||||
return viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight;
|
||||
return viewportWidth == other.viewportWidth && viewportHeight == other.viewportHeight
|
||||
&& actualWidth == other.actualWidth && actualHeight == other.actualHeight;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.DSL.ShaderCompiler;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.Core.Contracts;
|
||||
using Ghost.Graphics.RenderGraphModule;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Ghost.Graphics.Utilities;
|
||||
@@ -159,7 +159,7 @@ internal class MeshRenderPass : IRenderPass
|
||||
}
|
||||
else
|
||||
{
|
||||
var shaderResult = ctx.ResourceDatabase.GetShaderReference(_shader);
|
||||
var shaderResult = ctx.ResourceManager.GetShaderReference(_shader);
|
||||
if (shaderResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get shader reference.");
|
||||
@@ -201,7 +201,6 @@ internal class MeshRenderPass : IRenderPass
|
||||
{
|
||||
using var stream = File.OpenRead(_textureFiles[i]);
|
||||
using var imageData = ImageResult.FromStream(stream, ColorComponents.RGBA);
|
||||
|
||||
var desc = new TextureDesc
|
||||
{
|
||||
Width = imageData.Width,
|
||||
@@ -227,7 +226,7 @@ internal class MeshRenderPass : IRenderPass
|
||||
|
||||
_sampler = ctx.ResourceAllocator.CreateSampler(in samplerDesc);
|
||||
|
||||
var meshResult = ctx.ResourceDatabase.GetMaterialReference(_material);
|
||||
var meshResult = ctx.ResourceManager.GetMaterialReference(_material);
|
||||
if (meshResult.IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to get material reference.");
|
||||
@@ -283,7 +282,7 @@ internal class MeshRenderPass : IRenderPass
|
||||
|
||||
builder.SetRenderFunc<BlitPassData>(static (data, ctx) =>
|
||||
{
|
||||
var r = ctx.ResourceDatabase.GetMaterialReference(data.blitMaterial);
|
||||
var r = ctx.ResourceManager.GetMaterialReference(data.blitMaterial);
|
||||
if (r.IsFailure)
|
||||
{
|
||||
return;
|
||||
@@ -292,12 +291,12 @@ internal class MeshRenderPass : IRenderPass
|
||||
ref var matRef = ref r.Value;
|
||||
var blitProps = new ShaderProperties_Hidden_Blit
|
||||
{
|
||||
mainTex = ctx.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||
mainTex = ctx.ResourceManager.ResourceDatabase.GetBindlessIndex(ctx.GetActualResource(data.source.AsResource())),
|
||||
sampler_mainTex = (uint)data.sampler.Value,
|
||||
};
|
||||
|
||||
matRef.SetPropertyCache(in blitProps).ThrowIfFailed();
|
||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceDatabase);
|
||||
matRef.UploadData(ctx.CommandBuffer, ctx.ResourceManager.ResourceDatabase);
|
||||
|
||||
ctx.CommandBuffer.SetRenderTargets([ctx.GetActualTexture(data.destination)], Handle<Texture>.Invalid);
|
||||
|
||||
@@ -308,20 +307,20 @@ internal class MeshRenderPass : IRenderPass
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup(IResourceDatabase resourceDatabase)
|
||||
public void Cleanup(IResourceManager resourceManager)
|
||||
{
|
||||
resourceDatabase.ReleaseMaterial(_blitMaterial);
|
||||
resourceManager.ReleaseMaterial(_blitMaterial);
|
||||
|
||||
resourceDatabase.ReleaseMaterial(_material);
|
||||
resourceDatabase.ReleaseShader(_shader);
|
||||
resourceDatabase.ReleaseMesh(_mesh);
|
||||
resourceDatabase.ReleaseSampler(_sampler);
|
||||
resourceManager.ReleaseMaterial(_material);
|
||||
resourceManager.ReleaseShader(_shader);
|
||||
resourceManager.ReleaseMesh(_mesh);
|
||||
resourceManager.ResourceDatabase.ReleaseSampler(_sampler);
|
||||
|
||||
if (_textures != null)
|
||||
{
|
||||
foreach (var texture in _textures)
|
||||
{
|
||||
resourceDatabase.ScheduleReleaseResource(texture.AsResource());
|
||||
resourceManager.ResourceDatabase.ScheduleReleaseResource(texture.AsResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -23,50 +24,6 @@ public struct RenderingConfig
|
||||
}
|
||||
}
|
||||
|
||||
public interface IFenceSynchronizer
|
||||
{
|
||||
uint CPUFenceValue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint GPUFenceValue
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint FrameIndex
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
uint MaxFrameLatency
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool WaitForGPUReady(int timeOut = -1);
|
||||
void SignalCPUReady();
|
||||
void WaitIdle();
|
||||
}
|
||||
|
||||
public interface IRenderSystem : IFenceSynchronizer, IDisposable
|
||||
{
|
||||
IGraphicsEngine GraphicsEngine
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
bool IsRunning
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void RequestSwapChainResize(ISwapChain swapChain, uint2 newSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application-level render system that orchestrates multiple renderers
|
||||
/// and handles frame synchronization
|
||||
@@ -130,11 +87,25 @@ internal class RenderSystem : IRenderSystem
|
||||
public RenderSystem(RenderingConfig config)
|
||||
{
|
||||
_config = config;
|
||||
_graphicsEngine = config.GraphicsAPI switch
|
||||
|
||||
switch (config.GraphicsAPI)
|
||||
{
|
||||
GraphicsAPI.Direct3D12 => new D3D12.D3D12GraphicsEngine(this),
|
||||
_ => throw new NotSupportedException($"Graphics API {config.GraphicsAPI} is not supported.")
|
||||
};
|
||||
case GraphicsAPI.Direct3D12:
|
||||
if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 19041))
|
||||
{
|
||||
_graphicsEngine = D3D12GraphicsEngineFactory.Create(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Fallback to Vulkan once it's implemented.
|
||||
throw new PlatformNotSupportedException("Direct3D12 requires Windows 10 version 2004 (build 19041) or later.");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"The specified graphics API '{config.GraphicsAPI}' is not supported.");
|
||||
}
|
||||
|
||||
// Create frame resources for synchronization
|
||||
_frameResources = new FrameResource[config.FrameBufferCount];
|
||||
|
||||
330
src/Runtime/Ghost.Graphics/ResourceManager.cs
Normal file
330
src/Runtime/Ghost.Graphics/ResourceManager.cs
Normal file
@@ -0,0 +1,330 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core.Graphics;
|
||||
using Ghost.Graphics.Core;
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
|
||||
namespace Ghost.Graphics;
|
||||
|
||||
public interface IResourceManager
|
||||
{
|
||||
IResourceAllocator ResourceAllocator
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
IResourceDatabase ResourceDatabase
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new mesh from the specified vertex and index data.
|
||||
/// </summary>
|
||||
/// <param name="vertices">A UnsafeList containing the vertices that define the geometry of the mesh. Must contain at least one vertex.</param>
|
||||
/// <param name="indices">A UnsafeList containing the indices that specify how vertices are connected to form primitives. Must contain at least one index.</param>
|
||||
/// <returns>An <see cref="Identifier{Mesh}"/> representing the newly created mesh.</returns>
|
||||
Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices);
|
||||
|
||||
/// <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>
|
||||
Handle<Material> CreateMaterial(Identifier<Shader> shader);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new shader and returns its unique identifier.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="Identifier{Shader}"/> representing the newly created shader.</returns>
|
||||
/// <param name="descriptor">The viewGroup containing the shader's properties and passes.</param>
|
||||
Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a mesh with the specified Handle exists.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to check for existence. Cannot be null.</param>
|
||||
/// <returns>true if a mesh with the specified Handle exists; otherwise, false.</returns>
|
||||
bool HasMesh(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the mesh associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to retrieve. Must refer to a valid mesh; otherwise, the behavior is undefined.</param>
|
||||
/// <returns>A result containing a reference to the mesh corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the mesh resource associated with the specified handle, freeing any resources held by it. Includes both CPU and GPU resources.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the mesh to release. Must refer to a mesh that was previously created and not already released.</param>
|
||||
void ReleaseMesh(Handle<Mesh> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a material with the specified handle exists in the collection.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to check for existence.</param>
|
||||
/// <returns>true if a material with the specified handle exists; otherwise, false.</returns>
|
||||
bool HasMaterial(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the material associated with the specified handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to retrieve. Must refer to a valid material.</param>
|
||||
/// <returns>A result containing a reference to the material corresponding to the specified handle, or an error status if the handle is invalid.</returns>
|
||||
RefResult<Material, Error> GetMaterialReference(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Releases the material associated with the specified handle, making it available for reuse or disposal.
|
||||
/// </summary>
|
||||
/// <param name="handle">The handle of the material to release. Must refer to a material that has been previously acquired.</param>
|
||||
void ReleaseMaterial(Handle<Material> handle);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a shader with the specified identifier exists in the collection.
|
||||
/// </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>
|
||||
bool HasShader(Identifier<Shader> id);
|
||||
|
||||
/// <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>
|
||||
/// <returns>A result containing a reference to the shader corresponding to the specified identifier, or an error status if the identifier is invalid.</returns>
|
||||
RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id);
|
||||
|
||||
/// <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>
|
||||
void ReleaseShader(Identifier<Shader> id);
|
||||
}
|
||||
|
||||
internal sealed class ResourceManager : IResourceManager, IDisposable
|
||||
{
|
||||
private readonly IResourceAllocator _resourceAllocator;
|
||||
private readonly IResourceDatabase _resourceDatabase;
|
||||
|
||||
private UnsafeSlotMap<Mesh> _meshes;
|
||||
private UnsafeSlotMap<Material> _materials;
|
||||
private UnsafeList<Shader> _shaders; // TODO: Use SlotMap?
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public IResourceAllocator ResourceAllocator => _resourceAllocator;
|
||||
public IResourceDatabase ResourceDatabase => _resourceDatabase;
|
||||
|
||||
public ResourceManager(IResourceAllocator resourceAllocator, IResourceDatabase resourceDatabase)
|
||||
{
|
||||
_resourceAllocator = resourceAllocator;
|
||||
_resourceDatabase = resourceDatabase;
|
||||
|
||||
_meshes = new UnsafeSlotMap<Mesh>(64, Allocator.Persistent);
|
||||
_materials = new UnsafeSlotMap<Material>(16, Allocator.Persistent);
|
||||
_shaders = new UnsafeList<Shader>(16, Allocator.Persistent);
|
||||
}
|
||||
|
||||
~ResourceManager()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public unsafe Handle<Mesh> CreateMesh(UnsafeList<Vertex> vertices, UnsafeList<uint> indices)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var vertexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(vertices.Count * sizeof(Vertex)),
|
||||
Stride = (uint)sizeof(Vertex),
|
||||
Usage = BufferUsage.Vertex | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var indexBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)(indices.Count * sizeof(uint)),
|
||||
Stride = sizeof(uint),
|
||||
Usage = BufferUsage.Index | BufferUsage.ShaderResource | BufferUsage.Raw,
|
||||
MemoryType = ResourceMemoryType.Default,
|
||||
};
|
||||
|
||||
var objectBufferDesc = new BufferDesc
|
||||
{
|
||||
Size = (uint)sizeof(PerObjectData),
|
||||
Stride = (uint)sizeof(PerObjectData),
|
||||
Usage = BufferUsage.Raw | BufferUsage.ShaderResource,
|
||||
MemoryType = ResourceMemoryType.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);
|
||||
}
|
||||
|
||||
public Handle<Material> CreateMaterial(Identifier<Shader> shader)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var material = new Material();
|
||||
if (material.SetShader(shader, this) != Error.None)
|
||||
{
|
||||
return Handle<Material>.Invalid;
|
||||
}
|
||||
|
||||
var id = _materials.Add(material, out var generation);
|
||||
return new Handle<Material>(id, generation);
|
||||
}
|
||||
|
||||
public Identifier<Shader> CreateGraphicsShader(ShaderDescriptor descriptor)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
var shader = new Shader(descriptor);
|
||||
|
||||
var id = _shaders.Count;
|
||||
_shaders.Add(shader);
|
||||
return new Identifier<Shader>(id);
|
||||
}
|
||||
|
||||
public bool HasMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _meshes.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public RefResult<Mesh, Error> GetMeshReference(Handle<Mesh> handle)
|
||||
{
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Mesh, Error>.Success(ref mesh);
|
||||
}
|
||||
|
||||
public void ReleaseMesh(Handle<Mesh> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var mesh = ref _meshes.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseResource(mesh);
|
||||
_meshes.Remove(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public bool HasMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return _materials.Contains(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public RefResult<Material, Error> GetMaterialReference(Handle<Material> handle)
|
||||
{
|
||||
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Material, Error>.Success(ref material);
|
||||
}
|
||||
|
||||
public void ReleaseMaterial(Handle<Material> handle)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
ref var material = ref _materials.GetElementReferenceAt(handle.ID, handle.Generation, out var exist);
|
||||
if (!exist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseResource(material);
|
||||
_materials.Remove(handle.ID, handle.Generation);
|
||||
}
|
||||
|
||||
public bool HasShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
return id.Value >= 0 && id.Value < _shaders.Count;
|
||||
}
|
||||
|
||||
public RefResult<Shader, Error> GetShaderReference(Identifier<Shader> id)
|
||||
{
|
||||
if (!HasShader(id))
|
||||
{
|
||||
return Error.NotFound;
|
||||
}
|
||||
|
||||
return RefResult<Shader, Error>.Success(ref _shaders[id.Value]);
|
||||
}
|
||||
|
||||
public void ReleaseShader(Identifier<Shader> id)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!HasShader(id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var shader = ref _shaders[id.Value]!;
|
||||
ReleaseResource(shader);
|
||||
}
|
||||
|
||||
private void ReleaseResource<T>(T resource)
|
||||
where T : IResourceReleasable
|
||||
{
|
||||
resource.ReleaseResource(_resourceDatabase);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var mesh in _meshes)
|
||||
{
|
||||
ReleaseResource(mesh);
|
||||
}
|
||||
|
||||
foreach (var material in _materials)
|
||||
{
|
||||
ReleaseResource(material);
|
||||
}
|
||||
|
||||
foreach (var shader in _shaders)
|
||||
{
|
||||
ReleaseResource(shader);
|
||||
}
|
||||
|
||||
_meshes.Dispose();
|
||||
_materials.Dispose();
|
||||
_shaders.Dispose();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using Ghost.Graphics.RHI;
|
||||
using Misaki.HighPerformance.LowLevel.Buffer;
|
||||
using Misaki.HighPerformance.LowLevel.Collections;
|
||||
using Misaki.HighPerformance.Mathematics;
|
||||
using Ghost.Graphics.Core;
|
||||
|
||||
namespace Ghost.Graphics.Utilities;
|
||||
|
||||
public unsafe static class MeshBuilder
|
||||
public static unsafe class MeshBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a unit cube centered at the origin with size 1.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user