From ba8694ed0c7568ec8d664fa0d90f29dfdc894d1c Mon Sep 17 00:00:00 2001 From: Misaki Date: Fri, 8 May 2026 15:37:30 +0900 Subject: [PATCH] Add shader property reflection and resource handles Refactor shader property system to support runtime reflection via ShaderPropertyType and ShaderPropertyFieldInfo. Introduce strongly-typed Texture2D/3D and Buffer handle structs. Update ShaderPropertiesGenerator to emit field metadata and register it. Move mesh content structs to AssetManager.Mesh.cs and mark as internal. Update DSLShaderCompiler and registry for new property API. Remove obsolete files and clean up namespaces. Add sample TestShaderProperty struct. --- .../ShaderCompiler/DSLShaderCompiler.cs | 12 +- .../Ghost.Core/Graphics/BindlessHandles.cs | 36 +++ .../Graphics/ShaderPropertyRegistry.cs | 59 ++++- src/Runtime/Ghost.Engine/AssetManager.Mesh.cs | 46 ++++ src/Runtime/Ghost.Engine/EngineCore.cs | 10 + src/Runtime/Ghost.Engine/MeshContent.cs | 47 ---- src/Runtime/Ghost.Engine/TestSetup.cs | 13 - src/Runtime/Ghost.Entities/SharedComponent.cs | 1 - .../ComponentSerializationGenerator.cs | 229 ------------------ .../ShaderPropertiesGenerator.cs | 46 +++- 10 files changed, 189 insertions(+), 310 deletions(-) create mode 100644 src/Runtime/Ghost.Core/Graphics/BindlessHandles.cs delete mode 100644 src/Runtime/Ghost.Engine/MeshContent.cs delete mode 100644 src/Runtime/Ghost.Engine/TestSetup.cs delete mode 100644 src/Runtime/Ghost.Generator/ComponentSerializationGenerator.cs diff --git a/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs b/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs index 3524960..06f43a0 100644 --- a/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs +++ b/src/Editor/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs @@ -101,7 +101,7 @@ public static class DSLShaderCompiler var pass = semantics.passes![i]; var localPipeline = MeragePipeline(pass.localPipeline, PipelineState.Default); - var result = BuildFinalShaderCode(pass.amplificationShader.shaderPath, pass.includes.AsSpan(), pass.hlsl, propertyInfo.code); + var result = BuildFinalShaderCode(pass.amplificationShader.shaderPath, pass.includes.AsSpan(), pass.hlsl, propertyInfo.Code); if (result.IsFailure) { return Result.Failure($"Failed to build shader code for pass '{pass.name}': {result.Message}"); @@ -109,7 +109,7 @@ public static class DSLShaderCompiler var amplificationShaderCode = new ShaderCode { code = result.Value, entryPoint = pass.amplificationShader.entry }; - result = BuildFinalShaderCode(pass.meshShader.shaderPath, pass.includes.AsSpan(), pass.hlsl, propertyInfo.code); + result = BuildFinalShaderCode(pass.meshShader.shaderPath, pass.includes.AsSpan(), pass.hlsl, propertyInfo.Code); if (result.IsFailure) { return Result.Failure($"Failed to build shader code for pass '{pass.name}': {result.Message}"); @@ -117,7 +117,7 @@ public static class DSLShaderCompiler var meshShaderCode = new ShaderCode { code = result.Value, entryPoint = pass.meshShader.entry }; - result = BuildFinalShaderCode(pass.pixelShader.shaderPath, pass.includes.AsSpan(), pass.hlsl, propertyInfo.code); + result = BuildFinalShaderCode(pass.pixelShader.shaderPath, pass.includes.AsSpan(), pass.hlsl, propertyInfo.Code); if (result.IsFailure) { return Result.Failure($"Failed to build shader code for pass '{pass.name}': {result.Message}"); @@ -142,7 +142,7 @@ public static class DSLShaderCompiler var descriptor = new GraphicsShaderDescriptor { Name = semantics.name, - PropertyBufferSize = propertyInfo.size, + PropertyBufferSize = propertyInfo.Size, ShaderModel = semantics.shaderModel, Passes = passes @@ -302,7 +302,7 @@ public static class DSLShaderCompiler var shaderCodes = new ShaderCode[semantics.entryPoints.Count]; for (var i = 0; i < shaderCodes.Length; i++) { - var result = BuildFinalShaderCode(semantics.entryPoints[i].shaderPath, semantics.includes.AsSpan(), semantics.hlsl, propertyInfo.code); + var result = BuildFinalShaderCode(semantics.entryPoints[i].shaderPath, semantics.includes.AsSpan(), semantics.hlsl, propertyInfo.Code); if (result.IsFailure) { return Result.Failure($"Failed to build shader code for entry point '{semantics.entryPoints[i].entry}': {result.Message}"); @@ -314,7 +314,7 @@ public static class DSLShaderCompiler return new ComputeShaderDescriptor { Name = semantics.name, - PropertyBufferSize = propertyInfo.size, + PropertyBufferSize = propertyInfo.Size, ShaderModel = semantics.shaderModel, ShaderCodes = shaderCodes, Defines = semantics.defines?.ToArray() ?? Array.Empty(), diff --git a/src/Runtime/Ghost.Core/Graphics/BindlessHandles.cs b/src/Runtime/Ghost.Core/Graphics/BindlessHandles.cs new file mode 100644 index 0000000..adfb4d9 --- /dev/null +++ b/src/Runtime/Ghost.Core/Graphics/BindlessHandles.cs @@ -0,0 +1,36 @@ +using System.Runtime.InteropServices; + +namespace Ghost.Core.Graphics; + +[StructLayout(LayoutKind.Sequential)] +public readonly struct Texture2DHandle +{ + public readonly uint DescriptorIndex; + + public Texture2DHandle(uint descriptorIndex) + { + DescriptorIndex = descriptorIndex; + } +} + +[StructLayout(LayoutKind.Sequential)] +public readonly struct Texture3DHandle +{ + public readonly uint DescriptorIndex; + + public Texture3DHandle(uint descriptorIndex) + { + DescriptorIndex = descriptorIndex; + } +} + +[StructLayout(LayoutKind.Sequential)] +public readonly struct BufferHandle +{ + public readonly uint DescriptorIndex; + + public BufferHandle(uint descriptorIndex) + { + DescriptorIndex = descriptorIndex; + } +} diff --git a/src/Runtime/Ghost.Core/Graphics/ShaderPropertyRegistry.cs b/src/Runtime/Ghost.Core/Graphics/ShaderPropertyRegistry.cs index b4b64cd..6596c4f 100644 --- a/src/Runtime/Ghost.Core/Graphics/ShaderPropertyRegistry.cs +++ b/src/Runtime/Ghost.Core/Graphics/ShaderPropertyRegistry.cs @@ -1,20 +1,69 @@ namespace Ghost.Core.Graphics; +public enum ShaderPropertyType +{ + Unknown, + Float, + Float2, + Float3, + Float4, + Int, + UInt, + Float4x4, + Texture2D, + Texture3D, + Buffer +} + +public readonly struct ShaderPropertyFieldInfo +{ + public string Name + { + get; init; + } + + public ShaderPropertyType Type + { + get; init; + } + + public int Offset + { + get; init; + } +} + #if DEBUG || GHOST_EDITOR public struct ShaderPropertyInfo { - public string shaderName; - public string code; - public uint size; + public string ShaderName + { + get; init; + } + + public string Code + { + get; init; + } + + public uint Size + { + get; init; + } + + public ShaderPropertyFieldInfo[] Fields + { + get; init; + } } public static class ShaderPropertiesRegistry { private static readonly Dictionary s_nameToCode = new Dictionary(StringComparer.Ordinal); - public static void Register(string name, string code, uint size) + public static void Register(string name, string code, uint size, ShaderPropertyFieldInfo[] fields) { - s_nameToCode[name] = new ShaderPropertyInfo { shaderName = name, code = code, size = size }; + s_nameToCode[name] = new ShaderPropertyInfo { ShaderName = name, Code = code, Size = size, Fields = fields }; } public static bool TryGetInfo(string name, out ShaderPropertyInfo info) diff --git a/src/Runtime/Ghost.Engine/AssetManager.Mesh.cs b/src/Runtime/Ghost.Engine/AssetManager.Mesh.cs index 9ec5e96..81eed67 100644 --- a/src/Runtime/Ghost.Engine/AssetManager.Mesh.cs +++ b/src/Runtime/Ghost.Engine/AssetManager.Mesh.cs @@ -3,11 +3,56 @@ using Ghost.Graphics; using Ghost.Graphics.Core; using Ghost.Graphics.RHI; using Ghost.Graphics.Utilities; +using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics.Geometry; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Ghost.Engine; +[StructLayout(LayoutKind.Sequential)] +internal struct MeshContentHeader +{ + public const uint MAGIC = 0x48534D47; // GMSH + public const uint VERSION = 1; + + public uint magic; + public uint version; + + public uint vertexCount; + public uint indexCount; + public uint materialPartCount; + public uint meshletCount; + public uint meshletGroupCount; + public uint meshletHierarchyNodeCount; + public uint meshletVertexCount; + public uint meshletTriangleCount; + public uint materialSlotCount; + public uint lodLevelCount; + + public float3 boundsMin; + public float3 boundsMax; + + public ulong vertexOffset; + public ulong indexOffset; + public ulong materialPartOffset; + public ulong meshletOffset; + public ulong meshletGroupOffset; + public ulong meshletHierarchyNodeOffset; + public ulong meshletVertexOffset; + public ulong meshletTriangleOffset; +} + +[StructLayout(LayoutKind.Sequential)] +internal struct MeshContentMaterialPart +{ + public int materialIndex; + public int indexStart; + public int indexCount; + public int vertexStart; + public int vertexCount; +} + internal unsafe partial class AssetEntry { private sealed unsafe class MeshParsedData @@ -225,6 +270,7 @@ internal unsafe partial class AssetEntry private void OnMeshUploadComplete(ResourceStreamingContext context) { var (oldHandle, newHandle) = GetStorage<(Handle, Handle)>(); + // FIX: Do not reaplce the mesh, replace the underlying buffers and data instead because we are using persistent gpu scene. Replacing the mesh does not update the gpu scene at all. var actualHandle = context.ResourceManager.ReplaceMesh(oldHandle, newHandle); if (actualHandle.IsInvalid) { diff --git a/src/Runtime/Ghost.Engine/EngineCore.cs b/src/Runtime/Ghost.Engine/EngineCore.cs index 96657e7..b891508 100644 --- a/src/Runtime/Ghost.Engine/EngineCore.cs +++ b/src/Runtime/Ghost.Engine/EngineCore.cs @@ -1,6 +1,8 @@ +using Ghost.Core.Graphics; using Ghost.Engine.RenderPipeline; using Ghost.Graphics; using Misaki.HighPerformance.Jobs; +using Misaki.HighPerformance.Mathematics; namespace Ghost.Engine; @@ -57,3 +59,11 @@ public sealed partial class EngineCore : IDisposable _jobScheduler.Dispose(); } } + +[GenerateShaderProperty("TestShader")] +public partial struct TestShaderProperty +{ + public Texture2DHandle texture; + public uint someValue; + public float3 otherValue; +} \ No newline at end of file diff --git a/src/Runtime/Ghost.Engine/MeshContent.cs b/src/Runtime/Ghost.Engine/MeshContent.cs deleted file mode 100644 index 0db59ff..0000000 --- a/src/Runtime/Ghost.Engine/MeshContent.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Misaki.HighPerformance.Mathematics; -using System.Runtime.InteropServices; - -namespace Ghost.Engine; - -[StructLayout(LayoutKind.Sequential)] -public struct MeshContentHeader -{ - public const uint MAGIC = 0x48534D47; // GMSH - public const uint VERSION = 1; - - public uint magic; - public uint version; - - public uint vertexCount; - public uint indexCount; - public uint materialPartCount; - public uint meshletCount; - public uint meshletGroupCount; - public uint meshletHierarchyNodeCount; - public uint meshletVertexCount; - public uint meshletTriangleCount; - public uint materialSlotCount; - public uint lodLevelCount; - - public float3 boundsMin; - public float3 boundsMax; - - public ulong vertexOffset; - public ulong indexOffset; - public ulong materialPartOffset; - public ulong meshletOffset; - public ulong meshletGroupOffset; - public ulong meshletHierarchyNodeOffset; - public ulong meshletVertexOffset; - public ulong meshletTriangleOffset; -} - -[StructLayout(LayoutKind.Sequential)] -public struct MeshContentMaterialPart -{ - public int materialIndex; - public int indexStart; - public int indexCount; - public int vertexStart; - public int vertexCount; -} diff --git a/src/Runtime/Ghost.Engine/TestSetup.cs b/src/Runtime/Ghost.Engine/TestSetup.cs deleted file mode 100644 index 2f36400..0000000 --- a/src/Runtime/Ghost.Engine/TestSetup.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Ghost.Engine; - -internal class TestSetup : IDisposable -{ - public void Dispose() - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/Runtime/Ghost.Entities/SharedComponent.cs b/src/Runtime/Ghost.Entities/SharedComponent.cs index 3868899..677ac92 100644 --- a/src/Runtime/Ghost.Entities/SharedComponent.cs +++ b/src/Runtime/Ghost.Entities/SharedComponent.cs @@ -2,7 +2,6 @@ using Ghost.Core; using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Utilities; -using System.Diagnostics; namespace Ghost.Entities; diff --git a/src/Runtime/Ghost.Generator/ComponentSerializationGenerator.cs b/src/Runtime/Ghost.Generator/ComponentSerializationGenerator.cs deleted file mode 100644 index 6b89227..0000000 --- a/src/Runtime/Ghost.Generator/ComponentSerializationGenerator.cs +++ /dev/null @@ -1,229 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Immutable; -using System.Linq; -using System.Text; - -namespace Ghost.Generator -{ - [Generator] - public class ComponentSerializationGenerator : IIncrementalGenerator - { - private string GetJsonWriteCall(ITypeSymbol type, string fieldName) - { - switch (type.SpecialType) - { - case SpecialType.System_Byte: - case SpecialType.System_SByte: - case SpecialType.System_Int16: - case SpecialType.System_Int32: - case SpecialType.System_Int64: - case SpecialType.System_Single: - case SpecialType.System_Double: - case SpecialType.System_Decimal: - case SpecialType.System_UInt64: - case SpecialType.System_UInt16: - case SpecialType.System_UInt32: - case SpecialType.System_IntPtr: - case SpecialType.System_UIntPtr: - return $@"writer.WriteNumber(""{fieldName}"", value.{fieldName});"; - case SpecialType.System_Boolean: - return $@"writer.WriteBoolean(""{fieldName}"", value.{fieldName});"; - case SpecialType.System_Char: - return $@"writer.WriteString(""{fieldName}"", [value.{fieldName}]);"; - case SpecialType.System_String: - return $@"writer.WriteString(""{fieldName}"", value.{fieldName});"; - } - - return $@"writer.WritePropertyName(""{fieldName}""); global::System.Text.Json.JsonSerializer.Serialize(writer, value.{fieldName}, options);"; - } - - private string GetJsonReadCall(ITypeSymbol type) - { - switch (type.SpecialType) - { - case SpecialType.System_Byte: return "reader.GetByte()"; - case SpecialType.System_SByte: return "reader.GetSByte()"; - case SpecialType.System_Int16: return "reader.GetInt16()"; - case SpecialType.System_Int32: return "reader.GetInt32()"; - case SpecialType.System_Int64: return "reader.GetInt64()"; - case SpecialType.System_Single: return "reader.GetSingle()"; - case SpecialType.System_Double: return "reader.GetDouble()"; - case SpecialType.System_Decimal: return "reader.GetDecimal()"; - case SpecialType.System_UInt16: return "reader.GetUInt16()"; - case SpecialType.System_UInt32: return "reader.GetUInt32()"; - case SpecialType.System_UInt64: return "reader.GetUInt64()"; - // Note: the size of IntPtr and UIntPtr varies by platform, we use Int64/UInt64 to ensure compatibility - case SpecialType.System_IntPtr: return "reader.GetInt64()"; - case SpecialType.System_UIntPtr: return "reader.GetUInt64()"; - case SpecialType.System_Boolean: return "reader.GetBoolean()"; - case SpecialType.System_Char: return "reader.GetString()[0]"; - case SpecialType.System_String: return "reader.GetString()"; - } - - return $"global::System.Text.Json.JsonSerializer.Deserialize<{type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>(ref reader, options)"; - } - - private void GenerateJsonSerializer(SourceProductionContext context, ImmutableArray symbols) - { - var sbWrites = new StringBuilder(); - var sbReads = new StringBuilder(); - - foreach (var symbol in symbols) - { - var namespaceName = symbol.ContainingNamespace.ToDisplayString(); - var structName = symbol.Name; - var fullTypeName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - - // 1. Build Field Logic (Same as before) - sbWrites.Clear(); - sbReads.Clear(); - - var fields = symbol.GetMembers() - .OfType() - .Where(f => !f.IsStatic && f.DeclaredAccessibility == Accessibility.Public); - - foreach (var field in fields) - { - // Note: GetJsonWriteCall returns a string ending with ";" - var writeCall = GetJsonWriteCall(field.Type, field.Name); - if (writeCall != null) - { - sbWrites.Append(" "); // Indentation - sbWrites.AppendLine(writeCall); - } - - var readCall = GetJsonReadCall(field.Type); - if (readCall != null) - { - // Note the double quotes ""{field.Name}"" for the case string - sbReads.Append(" "); // Indentation - sbReads.AppendLine($@"case ""{field.Name}"": result.{field.Name} = {readCall}; break;"); - } - } - - // 2. The Main Template using $@ - // Watch the double braces {{ }} and double quotes "" "" - var sourceCode = $@"// -#nullable enable - -namespace {namespaceName} -{{ - public unsafe static class {structName}_Serializer - {{ - [global::System.Runtime.CompilerServices.ModuleInitializer] - internal static void Init() - {{ - var id = Ghost.Entities.ComponentTypeID<{fullTypeName}>.Value; - Ghost.Engine.IO.ComponentSerializerRegistry.Register(id, SerializeBinaryUnsafe, SerializeJsonUnsafe); - }} - - // --------------------------------------------------------- - // BINARY (Fast Path) - // --------------------------------------------------------- - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static unsafe void SerializeBinaryUnsafe(global::System.IO.BinaryWriter writer, void* value) - {{ - writer.Write(new global::System.ReadOnlySpan(value, sizeof({fullTypeName}))); - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static void SerializeBinary(this global::System.IO.BinaryWriter writer, ref {fullTypeName} value) - {{ - unsafe {{ writer.Write(new global::System.ReadOnlySpan(global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref value), sizeof({fullTypeName}))); }} - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static void DeserializeBinary(this global::System.IO.BinaryReader reader, ref {fullTypeName} value) - {{ - unsafe {{ reader.Read(new global::System.Span(global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref value), sizeof({fullTypeName}))); }} - }} - - // --------------------------------------------------------- - // JSON WRITE - // --------------------------------------------------------- - public static unsafe void SerializeJsonUnsafe(System.Text.Json.Utf8JsonWriter writer, void* ptr, System.Text.Json.JsonSerializerOptions? options) - {{ - SerializeJson(writer, ref *( {fullTypeName}*)ptr, options); - }} - - public static void SerializeJson(this global::System.Text.Json.Utf8JsonWriter writer, ref {fullTypeName} value, global::System.Text.Json.JsonSerializerOptions? options) - {{ - writer.WriteStartObject(); -{sbWrites} - writer.WriteEndObject(); - }} - - // --------------------------------------------------------- - // JSON READ - // --------------------------------------------------------- - public static {fullTypeName} DeserializeJson(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Text.Json.JsonSerializerOptions? options) - {{ - var result = default({fullTypeName}); - - if (reader.TokenType != global::System.Text.Json.JsonTokenType.StartObject) throw new global::System.Text.Json.JsonException(); - - while (reader.Read()) - {{ - if (reader.TokenType == global::System.Text.Json.JsonTokenType.EndObject) return result; - if (reader.TokenType != global::System.Text.Json.JsonTokenType.PropertyName) throw new global::System.Text.Json.JsonException(); - - var propName = reader.GetString(); - reader.Read(); - - switch (propName) - {{ -{sbReads} - default: reader.Skip(); break; - }} - }} - return result; - }} - }} -}}"; - - context.AddSource($"{structName}.Serializer.gen.cs", sourceCode); - } - } - - public void Initialize(IncrementalGeneratorInitializationContext context) - { - return; - - var componentCandidates = context.SyntaxProvider - .CreateSyntaxProvider( - predicate: (syntaxNode, _) => syntaxNode is StructDeclarationSyntax, - transform: (ctx, _) => - { - var structSyntax = (StructDeclarationSyntax)ctx.Node; - - if (!(ctx.SemanticModel.GetDeclaredSymbol(structSyntax) is INamedTypeSymbol symbol)) - { - return null; - } - - var compilation = ctx.SemanticModel.Compilation; - var iComponentSymbol = compilation.GetTypeByMetadataName("Ghost.Entities.IComponent"); - - if (iComponentSymbol == null) - { - return null; - } - - foreach (var iface in symbol.AllInterfaces) - { - if (SymbolEqualityComparer.Default.Equals(iface, iComponentSymbol)) - { - return symbol; - } - } - - return null; - }) - .Where(symbol => symbol != null) - .Collect(); - - context.RegisterSourceOutput(componentCandidates, GenerateJsonSerializer); - } - } -} diff --git a/src/Runtime/Ghost.Generator/ShaderPropertiesGenerator.cs b/src/Runtime/Ghost.Generator/ShaderPropertiesGenerator.cs index f4d2323..8647695 100644 --- a/src/Runtime/Ghost.Generator/ShaderPropertiesGenerator.cs +++ b/src/Runtime/Ghost.Generator/ShaderPropertiesGenerator.cs @@ -93,6 +93,10 @@ namespace Ghost.Generator var definedSymbol = $"__{info.Name.ToUpper()}_G_HLSL"; + var fieldsBuilder = new StringBuilder(); + fieldsBuilder.AppendLine($" public static readonly global::Ghost.Core.Graphics.ShaderPropertyFieldInfo[] ReflectionData = new global::Ghost.Core.Graphics.ShaderPropertyFieldInfo[]"); + fieldsBuilder.AppendLine(" {"); + var fields = info.TypeSymbol.GetMembers().OfType(); foreach (var field in fields) { @@ -103,6 +107,7 @@ namespace Ghost.Generator var hlslTypeAttribute = field.GetAttributes().FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "Ghost.Engine.Utilities.GenerateAsHLSLTypeAttribute"); var hlslType = string.Empty; + var shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Unknown"; if (hlslTypeAttribute == null) { @@ -110,34 +115,51 @@ namespace Ghost.Generator { case SpecialType.System_Single: hlslType = "float"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float"; break; case SpecialType.System_Int32: hlslType = "int"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Int"; break; case SpecialType.System_UInt32: hlslType = "uint"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.UInt"; break; default: var typeName = field.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); switch (typeName) { - case "global::System.Numerics.Vector2": + case "global::System.Numerics.Vector2" or "global::Misaki.HighPerformance.Mathematics.float2": hlslType = "float2"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float2"; break; - case "global::System.Numerics.Vector3": + case "global::System.Numerics.Vector3" or "global::Misaki.HighPerformance.Mathematics.float3": hlslType = "float3"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float3"; break; - case "global::System.Numerics.Vector4": + case "global::System.Numerics.Vector4" or "global::Misaki.HighPerformance.Mathematics.float4": hlslType = "float4"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float4"; break; - case "global::System.Numerics.Matrix3x3": - hlslType = "float3x3"; - break; - case "global::System.Numerics.Matrix4x4": + case "global::System.Numerics.Matrix4x4" or "global::Misaki.HighPerformance.Mathematics.float4x4": hlslType = "float4x4"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float4x4"; break; case "global::System.Numerics.Quaternion" or "global::Misaki.HighPerformance.Mathematics.quaternion": hlslType = "float4"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float4"; + break; + case "global::Ghost.Core.Graphics.Texture2DHandle": + hlslType = "uint"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Texture2D"; + break; + case "global::Ghost.Core.Graphics.Texture3DHandle": + hlslType = "uint"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Texture3D"; + break; + case "global::Ghost.Core.Graphics.BufferHandle": + hlslType = "uint"; + shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Buffer"; break; case var _ when typeName.StartsWith("global::Misaki.HighPerformance.Mathematics."): hlslType = typeName.Substring("global::Misaki.HighPerformance.Mathematics.".Length); @@ -167,8 +189,12 @@ namespace Ghost.Generator } codeBuilder.AppendLine($" {hlslType} {field.Name};"); + + fieldsBuilder.AppendLine($" new global::Ghost.Core.Graphics.ShaderPropertyFieldInfo {{ Name = \"{field.Name}\", Type = {shaderPropType}, Offset = (int)global::System.Runtime.InteropServices.Marshal.OffsetOf<{info.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>(\"{field.Name}\") }},"); } + fieldsBuilder.AppendLine(" };"); + var code = $@"// namespace {info.TypeSymbol.ContainingNamespace.ToDisplayString()} @@ -185,15 +211,17 @@ struct {info.Name} {codeBuilder} }}; # endif // {definedSymbol}""; - }} + +{fieldsBuilder} #endif + }} }}"; context.AddSource($"{info.TypeSymbol.Name}_HLSL.gen.cs", code); codeBuilder.Clear(); var typeFullName = info.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - registerBuilder.AppendLine($@" global::Ghost.Core.Graphics.ShaderPropertiesRegistry.Register(""{info.ShaderName}"", {typeFullName}.HLSL_SOURCE, (uint)sizeof({typeFullName}));"); + registerBuilder.AppendLine($@" global::Ghost.Core.Graphics.ShaderPropertiesRegistry.Register(""{info.ShaderName}"", {typeFullName}.HLSL_SOURCE, (uint)sizeof({typeFullName}), {typeFullName}.ReflectionData);"); } var registerTypeName = "g_shaderproperty_registeration";