forked from Misaki/GhostEngine
Update rendering architecture and resource management
Added a new `Ref<T>` struct for reference semantics. Added the `RenderGraph` system for managing rendering passes. Added the `RenderTexture` class for encapsulating GPU resources. Added `GraphicsBuffer` class for effective GPU resource management. Changed `CommandList` methods from public to internal for visibility control. Changed `IRenderPass` interface from internal to public for accessibility. Changed `GetData<T>()` in `ComponentObject.cs` to return `CompRef<T>`. Changed `GetComponent<T>()` in `EntityManager.cs` to return `CompRef<T>`. Changed `GetSingleton<T>()` in `World.cs` to use `CompRef<T>`. Changed `IQueryTypeParameter` to use `CompRef<T>` for consistency. Changed `QueryItem<T0>` and related structs to use `CompRef<T>`. Changed `Material` class to support bindless textures. Changed `Shader` class to support bindless rendering. Changed `Mesh` class to support bindless vertex and index buffer access. Updated documentation to reflect the new bindless rendering architecture.
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12.Utilities;
|
||||
using Misaki.HighPerformance.LowLevel.Helpers;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D.Fxc;
|
||||
using Win32.Graphics.Direct3D.Dxc;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
@@ -72,10 +68,16 @@ internal readonly struct CBufferInfo
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bindless shader implementation using SM 6.6 with ResourceDescriptorHeap
|
||||
/// and D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED
|
||||
/// Enhanced to support both bindless and regular texture binding for hybrid materials
|
||||
/// </summary>
|
||||
public unsafe class Shader : IDisposable
|
||||
{
|
||||
private ComPtr<ID3D12PipelineState> _pipelineState;
|
||||
private ComPtr<ID3D12RootSignature> _rootSignature;
|
||||
private ComPtr<ID3D12DescriptorHeap> _samplerHeap;
|
||||
|
||||
private readonly byte[] _vertexShaderBytecode;
|
||||
private readonly byte[] _pixelShaderBytecode;
|
||||
@@ -83,215 +85,338 @@ public unsafe class Shader : IDisposable
|
||||
private readonly List<CBufferInfo> _constantBuffers = new();
|
||||
private readonly List<PropertyInfo> _properties = new();
|
||||
private readonly Dictionary<string, int> _propertyNameToIdMap = new();
|
||||
|
||||
private readonly List<TextureInfo> _textures = new();
|
||||
private readonly Dictionary<string, int> _textureNameToIdMap = new();
|
||||
private readonly List<TextureInfo> _regularTextures = new(); // Add regular texture support
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
internal ConstPtr<ID3D12PipelineState> PipelineState => new(_pipelineState.Get());
|
||||
internal ConstPtr<ID3D12RootSignature> RootSignature => new(_rootSignature.Get());
|
||||
internal ConstPtr<ID3D12DescriptorHeap> SamplerHeap => new(_samplerHeap.Get());
|
||||
|
||||
internal IReadOnlyList<CBufferInfo> ConstantBuffers => _constantBuffers;
|
||||
internal IReadOnlyList<PropertyInfo> Properties => _properties;
|
||||
internal IReadOnlyList<TextureInfo> Textures => _textures;
|
||||
internal IReadOnlyList<TextureInfo> RegularTextures => _regularTextures; // Expose regular textures
|
||||
|
||||
//public Shader(string shaderPath)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
internal Shader(string shaderCode)
|
||||
public Shader(string shaderCode)
|
||||
{
|
||||
_vertexShaderBytecode = CompileShader(Encoding.UTF8.GetBytes(shaderCode), "VSMain", "vs_5_0");
|
||||
_pixelShaderBytecode = CompileShader(Encoding.UTF8.GetBytes(shaderCode), "PSMain", "ps_5_0");
|
||||
var (vsBytecode, vsReflection) = CompileShaderDXC(shaderCode, "VSMain", "vs_6_6");
|
||||
var (psBytecode, psReflection) = CompileShaderDXC(shaderCode, "PSMain", "ps_6_6");
|
||||
|
||||
PerformReflection(_vertexShaderBytecode);
|
||||
PerformReflection(_pixelShaderBytecode);
|
||||
_vertexShaderBytecode = vsBytecode;
|
||||
_pixelShaderBytecode = psBytecode;
|
||||
|
||||
CreateRootSignature();
|
||||
CreatePipelineStateObject();
|
||||
PerformDXCReflection(vsReflection);
|
||||
PerformDXCReflection(psReflection);
|
||||
|
||||
CreateBindlessRootSignature();
|
||||
CreatePipelineState();
|
||||
CreateSamplerHeap();
|
||||
}
|
||||
|
||||
~Shader()
|
||||
private (byte[] bytecode, ComPtr<IDxcBlob> reflection) CompileShaderDXC(string source, string entryPoint, string profile)
|
||||
{
|
||||
Dispose();
|
||||
using ComPtr<IDxcCompiler3> compiler = default;
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
|
||||
// Create DXC compiler and utils
|
||||
DxcCreateInstance(CLSID_DxcCompiler, __uuidof<IDxcCompiler3>(), compiler.GetVoidAddressOf());
|
||||
DxcCreateInstance(CLSID_DxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
|
||||
// Create source blob
|
||||
using ComPtr<IDxcBlobEncoding> sourceBlob = default;
|
||||
var sourceBytes = System.Text.Encoding.UTF8.GetBytes(source);
|
||||
fixed (byte* sourceBytesPtr = sourceBytes)
|
||||
{
|
||||
utils.Get()->CreateBlob(sourceBytesPtr, (uint)sourceBytes.Length, DXC_CP_UTF8, sourceBlob.GetAddressOf());
|
||||
}
|
||||
|
||||
// Prepare compilation arguments - NOTE: NO -Qstrip_reflect to keep reflection data
|
||||
var argsArray = new string[]
|
||||
{
|
||||
"-T", profile, // Target profile (vs_6_6, ps_6_6)
|
||||
"-E", entryPoint, // Entry point
|
||||
"-HV", "2021", // HLSL version 2021 (required for SM 6.6)
|
||||
"-enable-16bit-types", // Enable 16-bit types
|
||||
"-O3", // Optimization level
|
||||
"-Qstrip_debug" // Strip debug info but KEEP reflection
|
||||
};
|
||||
|
||||
// Convert to wide strings (DXC expects LPCWSTR)
|
||||
var wideArgs = new nuint[argsArray.Length];
|
||||
var argPointers = new IntPtr[argsArray.Length];
|
||||
|
||||
for (var i = 0; i < argsArray.Length; i++)
|
||||
{
|
||||
argPointers[i] = Marshal.StringToHGlobalUni(argsArray[i]);
|
||||
wideArgs[i] = (nuint)argPointers[i];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Compile shader
|
||||
using ComPtr<IDxcResult> result = default;
|
||||
fixed (nuint* argsPtr = wideArgs)
|
||||
{
|
||||
var buffer = new DxcBuffer
|
||||
{
|
||||
Ptr = sourceBlob.Get()->GetBufferPointer(),
|
||||
Size = sourceBlob.Get()->GetBufferSize(),
|
||||
Encoding = DXC_CP_UTF8
|
||||
};
|
||||
|
||||
compiler.Get()->Compile(&buffer, (char**)argsPtr, (uint)argsArray.Length, null, __uuidof<IDxcResult>(), result.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
// Check compilation result
|
||||
HResult hrStatus;
|
||||
result.Get()->GetStatus(&hrStatus);
|
||||
if (hrStatus.Failure)
|
||||
{
|
||||
// Get error messages
|
||||
using ComPtr<IDxcBlobEncoding> errorBlob = default;
|
||||
result.Get()->GetErrorBuffer(errorBlob.GetAddressOf());
|
||||
|
||||
if (errorBlob.Get() != null)
|
||||
{
|
||||
var errorMessage = Marshal.PtrToStringUni((IntPtr)errorBlob.Get()->GetBufferPointer());
|
||||
throw new Exception($"DXC shader compilation failed: {errorMessage}");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("DXC shader compilation failed with unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
// Get compiled bytecode
|
||||
using ComPtr<IDxcBlob> bytecodeBlob = default;
|
||||
result.Get()->GetResult(bytecodeBlob.GetAddressOf());
|
||||
|
||||
if (bytecodeBlob.Get() == null)
|
||||
{
|
||||
throw new Exception("DXC compilation succeeded but no bytecode was produced");
|
||||
}
|
||||
|
||||
// Get reflection data using DXC API
|
||||
using ComPtr<IDxcBlob> reflectionBlob = default;
|
||||
result.Get()->GetOutput(DxcOutKind.Reflection, __uuidof<IDxcBlob>(), reflectionBlob.GetVoidAddressOf(), null);
|
||||
|
||||
if (reflectionBlob.Get() == null)
|
||||
{
|
||||
throw new Exception("DXC compilation succeeded but no reflection data was produced");
|
||||
}
|
||||
|
||||
// Copy bytecode to managed array
|
||||
var bytecodeSize = (int)bytecodeBlob.Get()->GetBufferSize();
|
||||
var bytecode = new byte[bytecodeSize];
|
||||
|
||||
fixed (byte* bytecodePtr = bytecode)
|
||||
{
|
||||
Buffer.MemoryCopy(bytecodeBlob.Get()->GetBufferPointer(), bytecodePtr, bytecodeSize, bytecodeSize);
|
||||
}
|
||||
|
||||
// Return both bytecode and reflection blob (move ownership)
|
||||
return (bytecode, reflectionBlob.Move());
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Free allocated wide strings
|
||||
for (var i = 0; i < argPointers.Length; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal(argPointers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles HLSL source code from a string into shader bytecode.
|
||||
/// </summary>
|
||||
/// <param name="sourceCode">The string containing the HLSL code.</param>
|
||||
/// <param name="entryPoint">The name of the shader entry point function (e.g., "VSMain").</param>
|
||||
/// <param name="shaderProfile">The shader model to target (e.g., "vs_5_0", "ps_5_0").</param>
|
||||
/// <returns>A byte array containing the compiled shader bytecode.</returns>
|
||||
/// <exception cref="Exception">Thrown if shader compilation fails.</exception>
|
||||
public static unsafe byte[] CompileShader(ReadOnlySpan<byte> sourceCodeBytes, string entryPoint, string shaderProfile)
|
||||
private void CreateBindlessRootSignature()
|
||||
{
|
||||
using ComPtr<ID3DBlob> bytecodeBlob = default;
|
||||
using ComPtr<ID3DBlob> errorBlob = default;
|
||||
var device = GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr;
|
||||
|
||||
var entryPointBytes = Encoding.UTF8.GetBytes(entryPoint);
|
||||
var shaderProfileBytes = Encoding.UTF8.GetBytes(shaderProfile);
|
||||
|
||||
ThrowIfFailed(D3DCompile(
|
||||
sourceCodeBytes,
|
||||
entryPointBytes.AsSpan(),
|
||||
shaderProfileBytes.AsSpan(),
|
||||
CompileFlags.EnableStrictness | CompileFlags.Debug,
|
||||
bytecodeBlob.GetAddressOf(),
|
||||
errorBlob.GetAddressOf()
|
||||
));
|
||||
|
||||
var bytecode = new byte[bytecodeBlob.Get()->GetBufferSize()];
|
||||
Unsafe.CopyBlock(bytecode.AsSpan().GetPointer(), bytecodeBlob.Get()->GetBufferPointer(), (uint)bytecode.Length);
|
||||
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
private void CreateRootSignature()
|
||||
{
|
||||
// Calculate total root parameters: CBVs + 1 descriptor table for SRVs (if any textures)
|
||||
var totalRootParameters = _constantBuffers.Count + (_textures.Count > 0 ? 1 : 0);
|
||||
var rootParameters = new RootParameter1[totalRootParameters];
|
||||
// Calculate total root parameters: CBVs + Regular texture descriptor table + Sampler table
|
||||
var totalRootParams = _constantBuffers.Count + (_regularTextures.Count > 0 ? 1 : 0) + 1; // +1 for sampler
|
||||
var rootParameters = new RootParameter1[totalRootParams];
|
||||
|
||||
var parameterIndex = 0;
|
||||
|
||||
// Add CBV root parameters
|
||||
for (var i = 0; i < _constantBuffers.Count; i++)
|
||||
foreach (var cbufferInfo in _constantBuffers)
|
||||
{
|
||||
var cbufferInfo = _constantBuffers[i];
|
||||
Debug.Assert(i == cbufferInfo.RegisterSlot);
|
||||
|
||||
var rootParameter = new RootParameter1
|
||||
rootParameters[parameterIndex++] = new RootParameter1
|
||||
{
|
||||
ParameterType = RootParameterType.Cbv,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
Descriptor = new RootDescriptor1(cbufferInfo.RegisterSlot, 0),
|
||||
};
|
||||
|
||||
rootParameters[parameterIndex++] = rootParameter;
|
||||
}
|
||||
|
||||
// Add descriptor table for SRVs if we have textures
|
||||
if (_textures.Count > 0)
|
||||
// Add regular texture descriptor table if we have regular textures
|
||||
if (_regularTextures.Count > 0)
|
||||
{
|
||||
var ranges = new DescriptorRange1[1];
|
||||
ranges[0] = new DescriptorRange1
|
||||
var textureRanges = new DescriptorRange1[1];
|
||||
textureRanges[0] = new DescriptorRange1
|
||||
{
|
||||
RangeType = DescriptorRangeType.Srv,
|
||||
NumDescriptors = (uint)_textures.Count,
|
||||
NumDescriptors = (uint)_regularTextures.Count,
|
||||
BaseShaderRegister = 0, // Start from t0
|
||||
RegisterSpace = 0,
|
||||
Flags = DescriptorRangeFlags.None,
|
||||
OffsetInDescriptorsFromTableStart = 0
|
||||
};
|
||||
|
||||
fixed (DescriptorRange1* rangesPtr = ranges)
|
||||
fixed (DescriptorRange1* textureRangesPtr = textureRanges)
|
||||
{
|
||||
var rootParameter = new RootParameter1
|
||||
rootParameters[parameterIndex++] = new RootParameter1
|
||||
{
|
||||
ParameterType = RootParameterType.DescriptorTable,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
DescriptorTable = new RootDescriptorTable1(1, rangesPtr)
|
||||
DescriptorTable = new RootDescriptorTable1(1, textureRangesPtr)
|
||||
};
|
||||
|
||||
rootParameters[parameterIndex++] = rootParameter;
|
||||
}
|
||||
}
|
||||
|
||||
// Create static samplers for textures
|
||||
var staticSamplers = new StaticSamplerDescription[_textures.Count];
|
||||
for (var i = 0; i < _textures.Count; i++)
|
||||
// Sampler descriptor table (still needed for samplers)
|
||||
var samplerRanges = new DescriptorRange1[1];
|
||||
samplerRanges[0] = new DescriptorRange1
|
||||
{
|
||||
staticSamplers[i] = new StaticSamplerDescription
|
||||
RangeType = DescriptorRangeType.Sampler,
|
||||
NumDescriptors = 1,
|
||||
BaseShaderRegister = 0, // s0
|
||||
RegisterSpace = 0,
|
||||
Flags = DescriptorRangeFlags.None,
|
||||
OffsetInDescriptorsFromTableStart = 0
|
||||
};
|
||||
|
||||
fixed (DescriptorRange1* samplerRangesPtr = samplerRanges)
|
||||
{
|
||||
rootParameters[parameterIndex] = new RootParameter1
|
||||
{
|
||||
Filter = Filter.MinMagMipLinear,
|
||||
AddressU = TextureAddressMode.Wrap,
|
||||
AddressV = TextureAddressMode.Wrap,
|
||||
AddressW = TextureAddressMode.Wrap,
|
||||
MipLODBias = 0,
|
||||
MaxAnisotropy = 1,
|
||||
BorderColor = StaticBorderColor.OpaqueWhite,
|
||||
MinLOD = 0,
|
||||
MaxLOD = 0,
|
||||
ShaderRegister = (uint)i, // s0, s1, etc.
|
||||
RegisterSpace = 0,
|
||||
ShaderVisibility = ShaderVisibility.All
|
||||
ParameterType = RootParameterType.DescriptorTable,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
DescriptorTable = new RootDescriptorTable1(1, samplerRangesPtr)
|
||||
};
|
||||
}
|
||||
|
||||
var parameterCount = (uint)rootParameters.Length;
|
||||
var parameters = parameterCount > 0
|
||||
? (RootParameter*)Unsafe.AsPointer(ref rootParameters[0])
|
||||
: null;
|
||||
|
||||
var samplerCount = (uint)staticSamplers.Length;
|
||||
var samplers = samplerCount > 0
|
||||
? (StaticSamplerDescription*)Unsafe.AsPointer(ref staticSamplers[0])
|
||||
: null;
|
||||
|
||||
var rootSignatureDesc = new RootSignatureDescription(parameterCount, parameters, samplerCount, samplers)
|
||||
// Create root signature with the modern flag
|
||||
fixed (RootParameter1* rootParamsPtr = rootParameters)
|
||||
{
|
||||
Flags = RootSignatureFlags.AllowInputAssemblerInputLayout
|
||||
var rootSignatureDesc = new RootSignatureDescription1
|
||||
{
|
||||
NumParameters = (uint)rootParameters.Length,
|
||||
pParameters = rootParamsPtr,
|
||||
NumStaticSamplers = 0,
|
||||
pStaticSamplers = null,
|
||||
// Key difference: Use the modern flag for direct heap indexing
|
||||
Flags = RootSignatureFlags.AllowInputAssemblerInputLayout |
|
||||
RootSignatureFlags.CbvSrvUavHeapDirectlyIndexed
|
||||
};
|
||||
|
||||
var versionedDesc = new VersionedRootSignatureDescription
|
||||
{
|
||||
Version = RootSignatureVersion.V1_1,
|
||||
Desc_1_1 = rootSignatureDesc
|
||||
};
|
||||
|
||||
using ComPtr<ID3DBlob> signature = default;
|
||||
using ComPtr<ID3DBlob> error = default;
|
||||
|
||||
D3D12SerializeVersionedRootSignature(&versionedDesc, signature.GetAddressOf(), error.GetAddressOf());
|
||||
|
||||
device->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _rootSignature.GetVoidAddressOf());
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePipelineState()
|
||||
{
|
||||
var device = GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr;
|
||||
|
||||
fixed (byte* vsPtr = _vertexShaderBytecode)
|
||||
fixed (byte* psPtr = _pixelShaderBytecode)
|
||||
{
|
||||
var psoDesc = new GraphicsPipelineStateDescription
|
||||
{
|
||||
pRootSignature = _rootSignature.Get(),
|
||||
VS = new ShaderBytecode(vsPtr, (nuint)_vertexShaderBytecode.Length),
|
||||
PS = new ShaderBytecode(psPtr, (nuint)_pixelShaderBytecode.Length),
|
||||
InputLayout = D3D12PipelineResource.InputLayoutDescription,
|
||||
RasterizerState = RasterizerDescription.CullNone,
|
||||
BlendState = BlendDescription.Opaque,
|
||||
DepthStencilState = DepthStencilDescription.Default,
|
||||
SampleMask = uint.MaxValue,
|
||||
PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
|
||||
NumRenderTargets = 1,
|
||||
SampleDesc = new SampleDescription(1, 0),
|
||||
DSVFormat = Format.Unknown,
|
||||
};
|
||||
|
||||
psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
|
||||
|
||||
device->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), _pipelineState.GetVoidAddressOf());
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSamplerHeap()
|
||||
{
|
||||
var device = GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr;
|
||||
|
||||
// Create sampler heap
|
||||
var samplerHeapDesc = new DescriptorHeapDescription
|
||||
{
|
||||
Type = DescriptorHeapType.Sampler,
|
||||
NumDescriptors = 1,
|
||||
Flags = DescriptorHeapFlags.ShaderVisible
|
||||
};
|
||||
|
||||
using ComPtr<ID3DBlob> signature = default;
|
||||
using ComPtr<ID3DBlob> error = default;
|
||||
device->CreateDescriptorHeap(&samplerHeapDesc, __uuidof<ID3D12DescriptorHeap>(), _samplerHeap.GetVoidAddressOf());
|
||||
|
||||
ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, RootSignatureVersion.V1_0, signature.GetAddressOf(), error.GetAddressOf()));
|
||||
// Create default sampler
|
||||
var samplerDesc = new SamplerDescription
|
||||
{
|
||||
Filter = Filter.MinMagMipLinear,
|
||||
AddressU = TextureAddressMode.Wrap,
|
||||
AddressV = TextureAddressMode.Wrap,
|
||||
AddressW = TextureAddressMode.Wrap,
|
||||
MipLODBias = 0,
|
||||
MaxAnisotropy = 1,
|
||||
MinLOD = 0,
|
||||
MaxLOD = float.MaxValue
|
||||
};
|
||||
|
||||
GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _rootSignature.GetVoidAddressOf());
|
||||
// Set border color manually
|
||||
samplerDesc.BorderColor[0] = 0;
|
||||
samplerDesc.BorderColor[1] = 0;
|
||||
samplerDesc.BorderColor[2] = 0;
|
||||
samplerDesc.BorderColor[3] = 0;
|
||||
|
||||
var samplerHandle = _samplerHeap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||
device->CreateSampler(&samplerDesc, samplerHandle);
|
||||
}
|
||||
|
||||
private void CreatePipelineStateObject()
|
||||
private unsafe void PerformDXCReflection(ComPtr<IDxcBlob> reflectionBlob)
|
||||
{
|
||||
try
|
||||
// Create DXC utils to parse reflection data
|
||||
using ComPtr<IDxcUtils> utils = default;
|
||||
DxcCreateInstance(CLSID_DxcUtils, __uuidof<IDxcUtils>(), utils.GetVoidAddressOf());
|
||||
|
||||
// Create reflection interface from blob
|
||||
var reflectionData = new DxcBuffer
|
||||
{
|
||||
fixed (byte* vsPtr = _vertexShaderBytecode)
|
||||
fixed (byte* psPtr = _pixelShaderBytecode)
|
||||
{
|
||||
var psoDesc = new GraphicsPipelineStateDescription
|
||||
{
|
||||
pRootSignature = _rootSignature.Get(),
|
||||
VS = new ShaderBytecode(vsPtr, (nuint)_vertexShaderBytecode.Length),
|
||||
PS = new ShaderBytecode(psPtr, (nuint)_pixelShaderBytecode.Length),
|
||||
InputLayout = D3D12PipelineResource.InputLayoutDescription,
|
||||
RasterizerState = RasterizerDescription.CullNone,
|
||||
BlendState = BlendDescription.Opaque,
|
||||
DepthStencilState = DepthStencilDescription.Default,
|
||||
SampleMask = uint.MaxValue,
|
||||
PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
|
||||
NumRenderTargets = 1,
|
||||
SampleDesc = new SampleDescription(1, 0),
|
||||
DSVFormat = Format.Unknown,
|
||||
};
|
||||
Ptr = reflectionBlob.Get()->GetBufferPointer(),
|
||||
Size = reflectionBlob.Get()->GetBufferSize(),
|
||||
Encoding = DXC_CP_ACP
|
||||
};
|
||||
|
||||
psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
|
||||
|
||||
// Create the PSO
|
||||
GraphicsPipeline.GraphicsDevice.NativeDevice.Ptr->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), _pipelineState.GetVoidAddressOf());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void PerformReflection(Span<byte> byteCode)
|
||||
{
|
||||
using ComPtr<ID3D12ShaderReflection> reflection = default;
|
||||
fixed (void* codePtr = byteCode)
|
||||
utils.Get()->CreateReflection(&reflectionData, __uuidof<ID3D12ShaderReflection>(), reflection.GetVoidAddressOf());
|
||||
|
||||
if (reflection.Get() == null)
|
||||
{
|
||||
D3DReflect(codePtr, (nuint)byteCode.Length, __uuidof<ID3D12ShaderReflection>(), reflection.GetVoidAddressOf());
|
||||
throw new Exception("Failed to create shader reflection from DXC output");
|
||||
}
|
||||
|
||||
ShaderDescription shaderDesc;
|
||||
reflection.Get()->GetDesc(&shaderDesc);
|
||||
|
||||
var cbufferRegistry = _constantBuffers.ToDictionary(cb => cb.Name);
|
||||
var textureRegistry = _textures.ToDictionary(t => t.Name);
|
||||
var textureRegistry = _regularTextures.ToDictionary(t => t.Name);
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
@@ -352,7 +477,8 @@ public unsafe class Shader : IDisposable
|
||||
continue;
|
||||
}
|
||||
|
||||
// The root parameter index for textures is after all CBVs
|
||||
// ALL texture input slots are regular textures!
|
||||
// Bindless textures don't use explicit texture inputs - they use ResourceDescriptorHeap[index]
|
||||
var textureInfo = new TextureInfo
|
||||
{
|
||||
Name = textureName,
|
||||
@@ -361,25 +487,18 @@ public unsafe class Shader : IDisposable
|
||||
};
|
||||
|
||||
textureRegistry.Add(textureName, textureInfo);
|
||||
|
||||
// Add to the texture name-to-ID mapping
|
||||
var newId = _textures.Count;
|
||||
_textureNameToIdMap.Add(textureName, newId);
|
||||
}
|
||||
}
|
||||
|
||||
_constantBuffers.Clear();
|
||||
_constantBuffers.AddRange(cbufferRegistry.Values);
|
||||
|
||||
_textures.Clear();
|
||||
_textures.AddRange(textureRegistry.Values);
|
||||
|
||||
reflection.Dispose();
|
||||
_regularTextures.Clear();
|
||||
_regularTextures.AddRange(textureRegistry.Values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique, stable ID for a shader property.
|
||||
/// This should be called once and the ID cached for repeated use.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">The name of the property (e.g., "_Color").</param>
|
||||
/// <returns>The integer ID of the property, or -1 if not found.</returns>
|
||||
@@ -388,17 +507,6 @@ public unsafe class Shader : IDisposable
|
||||
return _propertyNameToIdMap.TryGetValue(propertyName, out var id) ? id : -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique, stable ID for a texture property.
|
||||
/// This should be called once and the ID cached for repeated use.
|
||||
/// </summary>
|
||||
/// <param name="textureName">The name of the texture (e.g., "_MainTex").</param>
|
||||
/// <returns>The integer ID of the texture, or -1 if not found.</returns>
|
||||
public int GetTextureId(string textureName)
|
||||
{
|
||||
return _textureNameToIdMap.TryGetValue(textureName, out var id) ? id : -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
@@ -408,11 +516,12 @@ public unsafe class Shader : IDisposable
|
||||
|
||||
_pipelineState.Dispose();
|
||||
_rootSignature.Dispose();
|
||||
_samplerHeap.Dispose();
|
||||
|
||||
_constantBuffers.Clear();
|
||||
_properties.Clear();
|
||||
_textures.Clear();
|
||||
_textureNameToIdMap.Clear();
|
||||
_propertyNameToIdMap.Clear();
|
||||
_regularTextures.Clear();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user