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:
@@ -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<string>(),
|
||||
|
||||
36
src/Runtime/Ghost.Core/Graphics/BindlessHandles.cs
Normal file
36
src/Runtime/Ghost.Core/Graphics/BindlessHandles.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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<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)
|
||||
|
||||
@@ -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<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);
|
||||
if (actualHandle.IsInvalid)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IFieldSymbol>();
|
||||
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 = $@"// <auto-generated/>
|
||||
|
||||
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";
|
||||
|
||||
Reference in New Issue
Block a user