Refactor and enhance codebase for maintainability

Refactored and reorganized the codebase to improve readability, performance, and maintainability. Introduced new interfaces and structs for better resource management, updated project configuration files, and refactored shader and graphics pipeline management. Improved error handling, code formatting, and removed unused code and namespaces. Updated DLL references and method signatures for consistency and maintainability.
This commit is contained in:
2025-10-22 18:46:39 +09:00
parent 6d1b510ac1
commit d2d9f5feb7
80 changed files with 2836 additions and 2198 deletions

View File

@@ -1,34 +0,0 @@
using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
namespace Ghost.Graphics.Data;
internal struct CBufferCache : IDisposable
{
public UnsafeArray<byte> CpuData
{
get;
set;
}
public Handle<GraphicsBuffer> GpuResource
{
get;
}
private readonly uint _alignedSize;
internal unsafe CBufferCache(Handle<GraphicsBuffer> buffer, uint bufferSize)
{
_alignedSize = (bufferSize + 255u) & ~255u;
CpuData = new((int)_alignedSize, Allocator.Persistent);
GpuResource = buffer;
}
public readonly void Dispose()
{
CpuData.Dispose();
}
}

View File

@@ -1,23 +1,93 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
using System.Numerics;
using Misaki.HighPerformance.Mathematics;
using System.Runtime.CompilerServices;
namespace Ghost.Graphics.Data;
public struct Material : IHandleType
internal struct CBufferCache : IResourceReleasable
{
internal CBufferCache _materialPropertiesCache;
private UnsafeArray<byte> _cpuData;
private Handle<GraphicsBuffer> _gpuResource;
private uint _alignedSize;
public Identifier<Shader> Shader
public readonly UnsafeArray<byte> CpuData => _cpuData;
public readonly Handle<GraphicsBuffer> GpuResource => _gpuResource;
public readonly uint AlignedSize => _alignedSize;
public unsafe CBufferCache(IResourceAllocator allocator, uint bufferSize)
{
get; internal set;
_alignedSize = (bufferSize + 255u) & ~255u;
_cpuData = new((int)AlignedSize, Allocator.Persistent);
var desc = new BufferDesc
{
Size = bufferSize,
Usage = BufferUsage.Constant,
MemoryType = ResourceMemoryType.Default,
};
_gpuResource = allocator.CreateBuffer(ref desc);
}
internal readonly void Dispose()
public void ReleaseResource(IResourceDatabase database)
{
_cpuData.Dispose();
database.ReleaseResource(GpuResource.AsResource());
_gpuResource = Handle<GraphicsBuffer>.Invalid;
_alignedSize = 0;
}
}
public struct Material : IResourceReleasable, IHandleType
{
private Identifier<Shader> _shader;
private UnsafeArray<CBufferCache> _materialPropertiesCache; // One per shader pass
public readonly Identifier<Shader> Shader => _shader;
public Material(Identifier<Shader> shader, IResourceAllocator allocator, IResourceDatabase database)
{
SetShader(shader, allocator, database);
}
internal ref CBufferCache GetPassCache(int passIndex)
{
return ref _materialPropertiesCache[passIndex];
}
public void SetShader(Identifier<Shader> shaderId, IResourceAllocator allocator, IResourceDatabase database)
{
if (!shaderId.IsValid)
{
throw new ArgumentException("Shader ID is invalid.");
}
_shader = shaderId;
var shader = database.GetShaderReference(shaderId);
_materialPropertiesCache = new UnsafeArray<CBufferCache>(shader.PassCount, Allocator.Persistent);
for (var i = 0; i < shader.PassCount; i++)
{
var pass = database.GetShaderPass(shader.GetPassKey(i));
var cbufferInfo = pass.PassPropertyInfo;
_materialPropertiesCache[i] = new CBufferCache(allocator, cbufferInfo.Size);
}
}
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
foreach (var cache in _materialPropertiesCache)
{
cache.ReleaseResource(database);
}
_materialPropertiesCache.Dispose();
}
}
@@ -25,46 +95,35 @@ public struct Material : IHandleType
public ref struct MaterialAccessor
{
private ref Material _materialData;
private ref Shader _shader;
private readonly ref Shader _shader;
private readonly IResourceDatabase _resourceDatabase;
internal MaterialAccessor(ref Material materialData, IResourceDatabase resourceDatabase)
public MaterialAccessor(Handle<Material> material, IResourceDatabase resourceDatabase)
{
_resourceDatabase = resourceDatabase;
_materialData = ref materialData;
_shader = ref resourceDatabase.GetShaderReference(materialData.Shader);
_materialData = ref resourceDatabase.GetMaterialReference(material);
_shader = ref resourceDatabase.GetShaderReference(_materialData.Shader);
}
private readonly unsafe void WriteToCache<T>(int propertyId, in T value)
private readonly unsafe void WriteToCache<T>(string propertyName, in T value)
where T : unmanaged
{
if (propertyId == -1)
foreach (var index in _shader.GetPropertyPassIndices(propertyName))
{
throw new ArgumentException("Property ID is invalid.");
var passKey = _shader.GetPassKey(index);
var pass = _resourceDatabase.GetShaderPass(passKey);
var propertyInfo = pass.GetPropertyInfo(propertyName);
if (propertyInfo.Size != sizeof(T))
{
throw new ArgumentException($"Property '{propertyName}' has a size mismatch. Expected {propertyInfo.Size} bytes, but got {sizeof(T)} bytes.");
}
ref var cache = ref _materialData.GetPassCache(index);
Unsafe.WriteUnaligned(ref cache.CpuData[propertyInfo.ByteOffset], value);
}
var propInfo = _shader.Properties[propertyId];
if (propInfo.Size != sizeof(T))
{
throw new ArgumentException($"Property '{propertyId}' has a size mismatch. Expected {propInfo.Size} bytes, but got {sizeof(T)} bytes.");
}
var cache = _materialData._materialPropertiesCache[propInfo.CBufferIndex];
Unsafe.WriteUnaligned(ref cache.CpuData[(int)propInfo.ByteOffset], value);
}
/// <summary>
/// Sets a float property in the material's constant buffer.
/// </summary>
/// <param name="propertyId">The ID of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetFloat(int propertyId, in float value)
{
WriteToCache(propertyId, in value);
}
/// <summary>
@@ -75,18 +134,7 @@ public ref struct MaterialAccessor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetFloat(string propertyName, in float value)
{
SetFloat(_shader.GetPropertyId(propertyName), in value);
}
/// <summary>
/// Sets a uint property in the material's constant buffer (useful for texture indices).
/// </summary>
/// <param name="propertyId">The ID of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetUInt(int propertyId, in uint value)
{
WriteToCache(propertyId, in value);
WriteToCache(propertyName, in value);
}
/// <summary>
@@ -97,18 +145,7 @@ public ref struct MaterialAccessor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetUInt(string propertyName, in uint value)
{
SetUInt(_shader.GetPropertyId(propertyName), in value);
}
/// <summary>
/// Sets a Vector property in the material's constant buffer.
/// </summary>
/// <param name="propertyId">The ID of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetVector(int propertyId, in Vector4 value)
{
WriteToCache(propertyId, in value);
WriteToCache(propertyName, in value);
}
/// <summary>
@@ -117,20 +154,9 @@ public ref struct MaterialAccessor
/// <param name="propertyName">The name of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetVector(string propertyName, in Vector4 value)
public readonly void SetVector(string propertyName, in float4 value)
{
SetVector(_shader.GetPropertyId(propertyName), in value);
}
/// <summary>
/// Sets a Matrix property in the material's constant buffer.
/// </summary>
/// <param name="propertyId">The ID of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetMatrix(int propertyId, in Matrix4x4 value)
{
WriteToCache(propertyId, in value);
WriteToCache(propertyName, in value);
}
/// <summary>
@@ -139,9 +165,9 @@ public ref struct MaterialAccessor
/// <param name="propertyName">The name of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void SetMatrix(string propertyName, in Matrix4x4 value)
public readonly void SetMatrix(string propertyName, in float4x4 value)
{
SetMatrix(_shader.GetPropertyId(propertyName), in value);
WriteToCache(propertyName, in value);
}
/// <summary>
@@ -186,8 +212,9 @@ public ref struct MaterialAccessor
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly void UploadMaterialData(ICommandBuffer cmb)
{
foreach (var cache in _materialData._materialPropertiesCache)
for (var i = 0; i < _shader.PassCount; i++)
{
ref var cache = ref _materialData.GetPassCache(i);
cmb.Upload(cache.GpuResource, cache.CpuData.AsSpan());
}
}

View File

@@ -1,4 +1,5 @@
using Ghost.Core;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities;
@@ -7,7 +8,7 @@ using Misaki.HighPerformance.Mathematics.Geometry;
namespace Ghost.Graphics.Data;
public struct Mesh : IHandleType
public struct Mesh : IResourceReleasable, IHandleType
{
public UnsafeList<Vertex> vertices;
public UnsafeList<uint> indices;
@@ -38,6 +39,14 @@ public struct Mesh : IHandleType
vertices.Dispose();
indices.Dispose();
}
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
ReleaseCpuResources();
database.ReleaseResource(vertexBuffer.AsResource());
database.ReleaseResource(indexBuffer.AsResource());
}
}
public static class MeshExtension

View File

@@ -134,7 +134,7 @@ public unsafe readonly ref struct RenderingContext
public void RenderMesh(Handle<Mesh> mesh, Handle<Material> material)
{
_cmd.DrawMesh(mesh, material);
//_cmd.DrawMesh(mesh, material);
}
public void ExecuteCopyCommands()

View File

@@ -1,11 +1,13 @@
using Ghost.Core;
using Ghost.Core.Contracts;
using Ghost.Core.Graphics;
using Misaki.HighPerformance.LowLevel.Buffer;
using Ghost.Graphics.RHI;
using Misaki.HighPerformance.LowLevel.Collections;
using System.Runtime.InteropServices;
namespace Ghost.Graphics.Data;
internal readonly struct TextureInfo
public readonly struct TextureInfo
{
public uint RegisterSlot
{
@@ -18,7 +20,7 @@ internal readonly struct TextureInfo
}
}
internal readonly struct PropertyInfo
public readonly struct PropertyInfo
{
public uint CBufferIndex
{
@@ -36,7 +38,7 @@ internal readonly struct PropertyInfo
}
}
internal readonly struct CBufferInfo
public readonly struct CBufferInfo
{
public uint Size
{
@@ -49,65 +51,117 @@ internal readonly struct CBufferInfo
}
}
internal struct ShaderPass
public readonly unsafe struct ShaderPass
{
// NOTE: This is for per pass cbuffer only. Global, per view, and per mesh cbuffers are fixed.
private readonly Dictionary<string, int> _propertyLookup;
private readonly UnsafeList<PropertyInfo> _properties;
internal readonly CBufferInfo PassPropertyInfo
{
get;
}
public ShaderPass(CBufferInfo info, UnsafeList<PropertyInfo> properties, Dictionary<string, int> propertyNameToIdMap)
{
PassPropertyInfo = info;
_properties = properties;
_propertyLookup = propertyNameToIdMap;
}
public readonly int GetPropertyId(string propertyName)
{
return _propertyLookup.TryGetValue(propertyName, out var id) ? id : -1;
}
public readonly PropertyInfo GetPropertyInfo(int id)
{
return _properties[id];
}
public readonly PropertyInfo GetPropertyInfo(string propertyName)
{
return _properties[GetPropertyId(propertyName)];
}
}
/// <summary>
/// A representation of a GPU shader, including its metadata about its resources.
/// A representation of a GPU shader, including all the passes it contains.
/// </summary>
// TODO: Multi pass and keyword support
public struct Shader : IIdentifierType
public readonly struct Shader : IResourceReleasable, IIdentifierType
{
private readonly ShaderDescriptor _descriptor;
private readonly ShaderPassKey[] _passIDs;
private readonly Dictionary<string, int> _passLookup; // pass name to index
private readonly Dictionary<string, List<int>> _propertyLookup; // property name to pass index (property name to list of pass indices that contain the property)
private CBufferInfo _perMaterialBufferInfo;
private UnsafeList<PropertyInfo> _properties;
private Dictionary<string, int> _propertyNameToIdMap;
public int PassCount => _passIDs.Length;
private bool _disposed;
internal CBufferInfo PerMaterialBufferInfo
internal Shader(ShaderDescriptor descriptor)
{
readonly get => _perMaterialBufferInfo;
set => _perMaterialBufferInfo = value;
}
_passIDs = new ShaderPassKey[descriptor.passes.Count];
_passLookup = new(descriptor.passes.Count);
_propertyLookup = new(descriptor.passes.Count);
internal readonly UnsafeList<PropertyInfo> Properties => _properties;
internal readonly Dictionary<string, int> PropertyNameToIdMap => _propertyNameToIdMap;
public Shader(ShaderDescriptor descriptor)
{
_descriptor = descriptor;
_properties = new(8, Allocator.Persistent);
_propertyNameToIdMap = new(8);
_disposed = false;
}
/// <summary>
/// Gets a unique, stable ID for a shader property.
/// </summary>
/// <param name="propertyName">The name of the shader property.</param>
/// <returns>The integer ID of the property, or -1 if not found.</returns>
public readonly int GetPropertyId(string propertyName)
{
return _propertyNameToIdMap.TryGetValue(propertyName, out var id) ? id : -1;
}
internal void Dispose()
{
if (_disposed)
for (var i = 0; i < descriptor.passes.Count; i++)
{
return;
var pass = descriptor.passes[i];
var passKey = new ShaderPassKey(pass.Identifier);
_passIDs[i] = passKey;
_passLookup[pass.Name] = i;
if (pass is FullPassDescriptor fullPass)
{
if (fullPass.properties == null)
{
continue;
}
foreach (var property in fullPass.properties)
{
ref var passIndices = ref CollectionsMarshal.GetValueRefOrAddDefault(_propertyLookup, property.name, out var exists);
if (!exists || passIndices == null)
{
passIndices = new List<int>();
}
passIndices.Add(i);
}
}
// TODO: handle inherited passes
}
}
public readonly ShaderPassKey GetPassKey(int index)
{
return _passIDs[index];
}
public readonly bool TryGetPassKey(string passName, out ShaderPassKey? passID)
{
var index = _passLookup.GetValueOrDefault(passName, -1);
if (index == -1)
{
passID = new(0);
return false;
}
_properties.Dispose();
passID = _passIDs[index];
return true;
}
_propertyNameToIdMap.Clear();
_propertyNameToIdMap = null!;
public readonly IReadOnlyCollection<int> GetPropertyPassIndices(string propertyName)
{
if (_propertyLookup.TryGetValue(propertyName, out var passIndices))
{
return passIndices;
}
_disposed = true;
return Array.Empty<int>();
}
void IResourceReleasable.ReleaseResource(IResourceDatabase database)
{
// Should we do something here?
}
}

View File

@@ -13,11 +13,11 @@ public struct Vertex
public const DXGI_FORMAT ALIGNED_FORMAT = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT;
public const int COUNT = 5;
public static readonly FixedString32 position = new("POSITION");
public static readonly FixedString32 normal = new("NORMAL");
public static readonly FixedString32 tangent = new("TANGENT");
public static readonly FixedString32 uv = new("TEXCOORD");
public static readonly FixedString32 color = new("COLOR");
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;