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.
This commit is contained in:
2026-05-08 15:37:30 +09:00
parent 80e820a858
commit ba8694ed0c
10 changed files with 189 additions and 310 deletions

View File

@@ -101,7 +101,7 @@ public static class DSLShaderCompiler
var pass = semantics.passes![i]; var pass = semantics.passes![i];
var localPipeline = MeragePipeline(pass.localPipeline, PipelineState.Default); 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) if (result.IsFailure)
{ {
return Result.Failure($"Failed to build shader code for pass '{pass.name}': {result.Message}"); 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 }; 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) if (result.IsFailure)
{ {
return Result.Failure($"Failed to build shader code for pass '{pass.name}': {result.Message}"); 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 }; 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) if (result.IsFailure)
{ {
return Result.Failure($"Failed to build shader code for pass '{pass.name}': {result.Message}"); 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 var descriptor = new GraphicsShaderDescriptor
{ {
Name = semantics.name, Name = semantics.name,
PropertyBufferSize = propertyInfo.size, PropertyBufferSize = propertyInfo.Size,
ShaderModel = semantics.shaderModel, ShaderModel = semantics.shaderModel,
Passes = passes Passes = passes
@@ -302,7 +302,7 @@ public static class DSLShaderCompiler
var shaderCodes = new ShaderCode[semantics.entryPoints.Count]; var shaderCodes = new ShaderCode[semantics.entryPoints.Count];
for (var i = 0; i < shaderCodes.Length; i++) 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) if (result.IsFailure)
{ {
return Result.Failure($"Failed to build shader code for entry point '{semantics.entryPoints[i].entry}': {result.Message}"); 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 return new ComputeShaderDescriptor
{ {
Name = semantics.name, Name = semantics.name,
PropertyBufferSize = propertyInfo.size, PropertyBufferSize = propertyInfo.Size,
ShaderModel = semantics.shaderModel, ShaderModel = semantics.shaderModel,
ShaderCodes = shaderCodes, ShaderCodes = shaderCodes,
Defines = semantics.defines?.ToArray() ?? Array.Empty<string>(), Defines = semantics.defines?.ToArray() ?? Array.Empty<string>(),

View File

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

View File

@@ -1,20 +1,69 @@
namespace Ghost.Core.Graphics; 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 #if DEBUG || GHOST_EDITOR
public struct ShaderPropertyInfo public struct ShaderPropertyInfo
{ {
public string shaderName; public string ShaderName
public string code; {
public uint size; get; init;
}
public string Code
{
get; init;
}
public uint Size
{
get; init;
}
public ShaderPropertyFieldInfo[] Fields
{
get; init;
}
} }
public static class ShaderPropertiesRegistry public static class ShaderPropertiesRegistry
{ {
private static readonly Dictionary<string, ShaderPropertyInfo> s_nameToCode = new Dictionary<string, ShaderPropertyInfo>(StringComparer.Ordinal); private static readonly Dictionary<string, ShaderPropertyInfo> s_nameToCode = new Dictionary<string, ShaderPropertyInfo>(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) public static bool TryGetInfo(string name, out ShaderPropertyInfo info)

View File

@@ -3,11 +3,56 @@ using Ghost.Graphics;
using Ghost.Graphics.Core; using Ghost.Graphics.Core;
using Ghost.Graphics.RHI; using Ghost.Graphics.RHI;
using Ghost.Graphics.Utilities; using Ghost.Graphics.Utilities;
using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Mathematics.Geometry; using Misaki.HighPerformance.Mathematics.Geometry;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ghost.Engine; 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 internal unsafe partial class AssetEntry
{ {
private sealed unsafe class MeshParsedData private sealed unsafe class MeshParsedData
@@ -225,6 +270,7 @@ internal unsafe partial class AssetEntry
private void OnMeshUploadComplete(ResourceStreamingContext context) private void OnMeshUploadComplete(ResourceStreamingContext context)
{ {
var (oldHandle, newHandle) = GetStorage<(Handle<Mesh>, Handle<Mesh>)>(); var (oldHandle, newHandle) = GetStorage<(Handle<Mesh>, Handle<Mesh>)>();
// 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); var actualHandle = context.ResourceManager.ReplaceMesh(oldHandle, newHandle);
if (actualHandle.IsInvalid) if (actualHandle.IsInvalid)
{ {

View File

@@ -1,6 +1,8 @@
using Ghost.Core.Graphics;
using Ghost.Engine.RenderPipeline; using Ghost.Engine.RenderPipeline;
using Ghost.Graphics; using Ghost.Graphics;
using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.Mathematics;
namespace Ghost.Engine; namespace Ghost.Engine;
@@ -57,3 +59,11 @@ public sealed partial class EngineCore : IDisposable
_jobScheduler.Dispose(); _jobScheduler.Dispose();
} }
} }
[GenerateShaderProperty("TestShader")]
public partial struct TestShaderProperty
{
public Texture2DHandle texture;
public uint someValue;
public float3 otherValue;
}

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Buffer;
using Misaki.HighPerformance.LowLevel.Collections; using Misaki.HighPerformance.LowLevel.Collections;
using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.LowLevel.Utilities;
using System.Diagnostics;
namespace Ghost.Entities; namespace Ghost.Entities;

View File

@@ -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<INamedTypeSymbol> 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<IFieldSymbol>()
.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 = $@"// <auto-generated/>
#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<byte>(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<byte>(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<byte>(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);
}
}
}

View File

@@ -93,6 +93,10 @@ namespace Ghost.Generator
var definedSymbol = $"__{info.Name.ToUpper()}_G_HLSL"; 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<IFieldSymbol>(); var fields = info.TypeSymbol.GetMembers().OfType<IFieldSymbol>();
foreach (var field in fields) 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 hlslTypeAttribute = field.GetAttributes().FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "Ghost.Engine.Utilities.GenerateAsHLSLTypeAttribute");
var hlslType = string.Empty; var hlslType = string.Empty;
var shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Unknown";
if (hlslTypeAttribute == null) if (hlslTypeAttribute == null)
{ {
@@ -110,34 +115,51 @@ namespace Ghost.Generator
{ {
case SpecialType.System_Single: case SpecialType.System_Single:
hlslType = "float"; hlslType = "float";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float";
break; break;
case SpecialType.System_Int32: case SpecialType.System_Int32:
hlslType = "int"; hlslType = "int";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Int";
break; break;
case SpecialType.System_UInt32: case SpecialType.System_UInt32:
hlslType = "uint"; hlslType = "uint";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.UInt";
break; break;
default: default:
var typeName = field.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var typeName = field.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
switch (typeName) switch (typeName)
{ {
case "global::System.Numerics.Vector2": case "global::System.Numerics.Vector2" or "global::Misaki.HighPerformance.Mathematics.float2":
hlslType = "float2"; hlslType = "float2";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float2";
break; break;
case "global::System.Numerics.Vector3": case "global::System.Numerics.Vector3" or "global::Misaki.HighPerformance.Mathematics.float3":
hlslType = "float3"; hlslType = "float3";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float3";
break; break;
case "global::System.Numerics.Vector4": case "global::System.Numerics.Vector4" or "global::Misaki.HighPerformance.Mathematics.float4":
hlslType = "float4"; hlslType = "float4";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float4";
break; break;
case "global::System.Numerics.Matrix3x3": case "global::System.Numerics.Matrix4x4" or "global::Misaki.HighPerformance.Mathematics.float4x4":
hlslType = "float3x3";
break;
case "global::System.Numerics.Matrix4x4":
hlslType = "float4x4"; hlslType = "float4x4";
shaderPropType = "global::Ghost.Core.Graphics.ShaderPropertyType.Float4x4";
break; break;
case "global::System.Numerics.Quaternion" or "global::Misaki.HighPerformance.Mathematics.quaternion": case "global::System.Numerics.Quaternion" or "global::Misaki.HighPerformance.Mathematics.quaternion":
hlslType = "float4"; 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; break;
case var _ when typeName.StartsWith("global::Misaki.HighPerformance.Mathematics."): case var _ when typeName.StartsWith("global::Misaki.HighPerformance.Mathematics."):
hlslType = typeName.Substring("global::Misaki.HighPerformance.Mathematics.".Length); hlslType = typeName.Substring("global::Misaki.HighPerformance.Mathematics.".Length);
@@ -167,8 +189,12 @@ namespace Ghost.Generator
} }
codeBuilder.AppendLine($" {hlslType} {field.Name};"); 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 = $@"// <auto-generated/> var code = $@"// <auto-generated/>
namespace {info.TypeSymbol.ContainingNamespace.ToDisplayString()} namespace {info.TypeSymbol.ContainingNamespace.ToDisplayString()}
@@ -185,15 +211,17 @@ struct {info.Name}
{codeBuilder} {codeBuilder}
}}; }};
# endif // {definedSymbol}""; # endif // {definedSymbol}"";
}}
{fieldsBuilder}
#endif #endif
}}
}}"; }}";
context.AddSource($"{info.TypeSymbol.Name}_HLSL.gen.cs", code); context.AddSource($"{info.TypeSymbol.Name}_HLSL.gen.cs", code);
codeBuilder.Clear(); codeBuilder.Clear();
var typeFullName = info.TypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); 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"; var registerTypeName = "g_shaderproperty_registeration";