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:
2025-08-01 21:34:48 +09:00
parent 1284bb17de
commit eafbfb2fa1
43 changed files with 3845 additions and 2183 deletions

View File

@@ -1,4 +1,5 @@
using Ghost.Graphics.D3D12;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Helpers;
using System.Runtime.CompilerServices;
@@ -13,19 +14,19 @@ internal struct CBufferCache : IDisposable
set;
}
public GraphicsResource GpuResource
public GraphicsBuffer GpuResource
{
get;
}
private readonly uint _alignedSize;
public unsafe CBufferCache(uint bufferSize)
internal unsafe CBufferCache(uint bufferSize)
{
_alignedSize = (bufferSize + 255u) & ~255u;
CpuData = new((int)_alignedSize, Allocator.Persistent);
GpuResource = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(_alignedSize);
GpuResource = GraphicsBuffer.Create(_alignedSize, GraphicsBuffer.Usage.Constant);
GpuResource.Name = "Material_CBufferCache";
UploadToGpu();
@@ -34,11 +35,12 @@ internal struct CBufferCache : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void UploadToGpu()
{
GpuResource.SetData(CpuData.AsSpan());
GpuResource.SetData(CpuData.AsSpan(), 0);
}
public readonly void Dispose()
{
CpuData.Dispose();
GpuResource.Dispose();
}
}

View File

@@ -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);

View File

@@ -1,48 +0,0 @@
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Helpers;
namespace Ghost.Graphics.Shading;
public enum ShaderPropertyType
{
Float,
Float2,
Float3,
Float4,
Color,
Matrix,
Texture2D,
Texture3D
}
public struct ShaderProperty : IDisposable
{
private UnsafeArray<byte> _value;
private FixedString128 _name;
private readonly uint _valueOffset;
internal readonly uint Offset => _valueOffset;
public readonly string Name => _name.Value;
public readonly ReadOnlySpan<byte> Value => _value.AsSpan();
public ShaderPropertyType PropertyType
{
get;
}
public ShaderProperty(Span<byte> value, uint offset, string name, ShaderPropertyType type)
{
_value = new(value.Length, Allocator.Persistent);
_valueOffset = offset;
_name = new(name);
PropertyType = type;
}
public void Dispose()
{
_value.Dispose();
_name.Dispose();
}
}