diff --git a/.editorconfig b/.editorconfig index 533c8ff..d3680b1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,4 +5,8 @@ csharp_preserve_single_line_blocks = true csharp_style_prefer_primary_constructors = false dotnet_sort_system_directives_first = false dotnet_separate_import_directive_groups = false + +dotnet_style_prefer_collection_expression = false +dotnet_style_collection_initializer = false + max_line_length = 400 \ No newline at end of file diff --git a/Ghost.Core/Graphics/PipelineState.cs b/Ghost.Core/Graphics/PipelineState.cs index 8a3e443..0c845be 100644 --- a/Ghost.Core/Graphics/PipelineState.cs +++ b/Ghost.Core/Graphics/PipelineState.cs @@ -81,4 +81,30 @@ public struct PipelineState Blend = Blend.Opaque, ColorMask = ColorWriteMask.All }; + + public readonly ulong GetHashCode64() + { + // 32-bit packed key for states controlled by material / overrides. + // layout: + // 0..3 Blend (4 bits) + // 4..6 Cull (3 bits) + // 7..10 DeafaultState (4 bits) + // 11 ZWrite (1 bit) + // 12..15 ColorMask (4 bits) + + var key = 0u; + key |= ((uint)Blend & 0xFu) << 0; + key |= ((uint)Cull & 0x7u) << 4; + key |= ((uint)ZTest & 0xFu) << 7; + key |= ((uint)ZWrite & 0x1u) << 11; + key |= ((uint)ColorMask & 0xFu) << 12; + + return key; + } + + public override readonly int GetHashCode() + { + var code64 = GetHashCode64(); + return ((int)code64) ^ (int)(code64 >> 32); + } } \ No newline at end of file diff --git a/Ghost.Core/Graphics/ShaderDescriptor.cs b/Ghost.Core/Graphics/ShaderDescriptor.cs index a49f0b4..3d16305 100644 --- a/Ghost.Core/Graphics/ShaderDescriptor.cs +++ b/Ghost.Core/Graphics/ShaderDescriptor.cs @@ -30,7 +30,7 @@ public struct ShaderEntryPoint public struct KeywordsGroup { public KeywordSpace space; - public List? keywords; + public List keywords; } public interface IPassDescriptor @@ -53,7 +53,7 @@ public struct PropertyDescriptor public object? defaultValue; } -public class FullPassDescriptor : IPassDescriptor +public class PassDescriptor : IPassDescriptor { public string uniqueIdentifier = string.Empty; public string name = string.Empty; @@ -70,19 +70,9 @@ public class FullPassDescriptor : IPassDescriptor public string Name => name; } -public class FallbackPassDescriptor : IPassDescriptor -{ - public string fallbackPassIdentifier = string.Empty; - public string name = string.Empty; - - public string Identifier => fallbackPassIdentifier; - public string Name => name; -} - public class ShaderDescriptor { public string name = string.Empty; - public string? generatedCodePath; public uint cbufferSize; public List globalProperties = new(); public List properties = new(); diff --git a/Ghost.Core/Handle.cs b/Ghost.Core/Handle.cs index eaf7df1..029eee1 100644 --- a/Ghost.Core/Handle.cs +++ b/Ghost.Core/Handle.cs @@ -1,9 +1,5 @@ namespace Ghost.Core; -public interface IHandleType; -public interface IIdentifierType; -public interface IKeyType; - public readonly struct Handle : IEquatable> { public int ID @@ -139,19 +135,19 @@ public readonly struct Identifier : IEquatable> public static implicit operator Identifier(int value) => new Identifier(value); } -public readonly struct Key +public readonly struct Key64 : IEquatable> { public ulong Value { get; } - public Key(ulong value) + public Key64(ulong value) { Value = value; } - public static Key Invalid => new(0); + public static Key64 Invalid => new(0); public bool IsValid => this != Invalid; public bool IsInvalid => this == Invalid; @@ -161,28 +157,86 @@ public readonly struct Key return Value.GetHashCode(); } - public readonly override bool Equals(object? obj) - { - return obj is Key id && Equals(id); - } - - public readonly bool Equals(Key other) + public readonly bool Equals(Key64 other) { return Value == other.Value; } - public readonly int CompareTo(Key other) + public readonly int CompareTo(Key64 other) { return Value.CompareTo(other.Value); } - public static bool operator ==(Key a, Key b) + public readonly override bool Equals(object? obj) + { + return obj is Key64 id && Equals(id); + } + + public override string ToString() + { + return Value.ToString("X16"); + } + + public static bool operator ==(Key64 a, Key64 b) { return a.Equals(b); } - public static bool operator !=(Key a, Key b) + public static bool operator !=(Key64 a, Key64 b) { return !a.Equals(b); } } + +public readonly struct Key128 : IEquatable> +{ + public UInt128 Value + { + get; + } + + public Key128(UInt128 value) + { + Value = value; + } + + public static Key128 Invalid => new(0); + + public bool IsValid => this != Invalid; + public bool IsInvalid => this == Invalid; + + public readonly override int GetHashCode() + { + return Value.GetHashCode(); + } + + public readonly bool Equals(Key128 other) + { + return Value == other.Value; + } + + public readonly int CompareTo(Key128 other) + { + return Value.CompareTo(other.Value); + } + + public readonly override bool Equals(object? obj) + { + return obj is Key128 id && Equals(id); + } + + public override string ToString() + { + return Value.ToString("X16"); + } + + public static bool operator ==(Key128 a, Key128 b) + { + return a.Equals(b); + } + + public static bool operator !=(Key128 a, Key128 b) + { + return !a.Equals(b); + } +} \ No newline at end of file diff --git a/Ghost.Core/Utilities/Hash.cs b/Ghost.Core/Utilities/Hash.cs new file mode 100644 index 0000000..521b93c --- /dev/null +++ b/Ghost.Core/Utilities/Hash.cs @@ -0,0 +1,44 @@ +using System.Runtime.CompilerServices; + +namespace Ghost.Core.Utilities; + +public static class Hash +{ + private const ulong _PRIME1 = 0xfa517d6985796b7bul; + private const ulong _PRIME2 = 0x589578278297b985ul; + private const ulong _PRIME3 = 0x221147a447814b73ul; + private const ulong _PRIME4 = 0x9e3779b97f4a7c15ul; // Golden Ratio + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Hash64(ulong a, ulong b) + { + return a ^ (b * _PRIME4 + (a << 6) + (a >> 2)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Hash64(ulong a, ulong b, ulong c) + { + ulong h1 = a * _PRIME1; + ulong h2 = b * _PRIME2; + ulong h3 = c * _PRIME3; + + ulong h = h1 ^ h2 ^ h3; + + h = (h ^ (h >> 33)) * _PRIME4; + return h ^ (h >> 29); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Hash64(ulong a, ulong b, ulong c, ulong d) + { + ulong h1 = a * _PRIME1; + ulong h2 = b * _PRIME2; + ulong h3 = c * _PRIME3; + ulong h4 = d * _PRIME4; + + ulong h = h1 ^ h2 ^ h3 ^ h4; + + h = (h ^ (h >> 33)) * _PRIME1; + return h ^ (h >> 29); + } +} \ No newline at end of file diff --git a/Ghost.Shader/AssemblyInfo.cs b/Ghost.DSL/AssemblyInfo.cs similarity index 100% rename from Ghost.Shader/AssemblyInfo.cs rename to Ghost.DSL/AssemblyInfo.cs diff --git a/Ghost.Shader/BuiltIn/Common.hlsl b/Ghost.DSL/BuiltIn/Common.hlsl similarity index 83% rename from Ghost.Shader/BuiltIn/Common.hlsl rename to Ghost.DSL/BuiltIn/Common.hlsl index ef62cda..2d22825 100644 --- a/Ghost.Shader/BuiltIn/Common.hlsl +++ b/Ghost.DSL/BuiltIn/Common.hlsl @@ -10,8 +10,6 @@ struct Vertex float4 color; }; -#define SIZEOF_VERTEX 80 // bytes - // Resource descriptor heap definitions #define GLOBAL_TEXTURE2D_HEAP ResourceDescriptorHeap @@ -50,7 +48,10 @@ struct Vertex #define SAMPLE_TEXTURE2D_LEVEL(texId, sampId, uv, level) SampleTexture2DLevel(texId, sampId, uv, level) #define SAMPLE_TEXTURE2D_ARRAY(texId, sampId, uvw) SampleTextureArray(texId, sampId, uvw) -#define MESH_SHADER_THREADS(x) [NumThreads(x, 1, 1)] + +#define MESH_SHADER_THREADS(x) NumThreads(x, 1, 1) +#define OUTPUT_TRIANGLE_TOPOLOGY OutputTopology("triangle") +#define OUTPUT_LINE_TOPOLOGY OutputTopology("line") static inline float4 SampleTexture2D(uint texId, uint sampId, float2 uv) @@ -76,24 +77,21 @@ static inline float4 SampleTextureArray(uint texId, uint sampId, float3 uvw) static inline Vertex LoadVertexData(uint vertexID, uint groupID, BYTE_ADDRESS_BUFFER vertexBuffer, BYTE_ADDRESS_BUFFER indexBuffer) { - // Fetch bindless buffers ByteAddressBuffer vertices = GET_BUFFER(vertexBuffer); ByteAddressBuffer indices = GET_BUFFER(indexBuffer); - + // Compute the triangle’s vertex indices uint indexOffset = (groupID * 3 + vertexID) * 4; // uint32 index uint vertexIndex = indices.Load(indexOffset); - - // Load vertex attributes - uint vertexOffset = vertexIndex * SIZEOF_VERTEX; - Vertex v; - v.position = asfloat(vertices.Load4(vertexOffset + 0)); - v.normal = asfloat(vertices.Load4(vertexOffset + 16)); - v.tangent = asfloat(vertices.Load4(vertexOffset + 32)); - v.uv = asfloat(vertices.Load4(vertexOffset + 48)); - v.color = asfloat(vertices.Load4(vertexOffset + 64)); - - return v; + + return vertices.Load(vertexIndex * sizeof(Vertex)); +} + +template +static inline T LoadData(BYTE_ADDRESS_BUFFER buffer, uint index) +{ + ByteAddressBuffer buf = GET_BUFFER(buffer); + return buf.Load(index * sizeof(T)); } #endif // BUILTIN_COMMON_HLSL diff --git a/Ghost.DSL/BuiltIn/Properties.hlsl b/Ghost.DSL/BuiltIn/Properties.hlsl new file mode 100644 index 0000000..3999d90 --- /dev/null +++ b/Ghost.DSL/BuiltIn/Properties.hlsl @@ -0,0 +1,36 @@ +#ifndef BUILTIN_PROPERTIES_HLSL +#define BUILTIN_PROPERTIES_HLSL + +#include "F:/csharp/GhostEngine/Ghost.DSL/BuiltIn/Common.hlsl" + +struct PushConstantData +{ + BYTE_ADDRESS_BUFFER globalBuffer; + BYTE_ADDRESS_BUFFER perViewBuffer; + BYTE_ADDRESS_BUFFER perObjectBuffer; + BYTE_ADDRESS_BUFFER perMaterialBuffer; +}; + +struct PerViewData +{ + float4x4 viewMatrix; + float4x4 projectionMatrix; + float3 cameraPosition; + float nearClip; + float3 cameraDirection; + float farClip; + float4 screenSize; // xy: size, zw: 1/size +}; + +struct PerObjectData +{ + float4x4 localToWorld; + float3 worldBoundsMin; + BYTE_ADDRESS_BUFFER vertexBuffer; + float3 worldBoundsMax; + BYTE_ADDRESS_BUFFER indexBuffer; +}; + +PushConstantData g_PushConstantData : register(b0); + +#endif // BUILTIN_PROPERTIES_HLSL diff --git a/Ghost.Shader/Generator/ShaderStructGenerator.cs b/Ghost.DSL/Generator/ShaderStructGenerator.cs similarity index 99% rename from Ghost.Shader/Generator/ShaderStructGenerator.cs rename to Ghost.DSL/Generator/ShaderStructGenerator.cs index 17484e9..d6c4c22 100644 --- a/Ghost.Shader/Generator/ShaderStructGenerator.cs +++ b/Ghost.DSL/Generator/ShaderStructGenerator.cs @@ -5,7 +5,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; -namespace Ghost.SDL.Generator; +namespace Ghost.DSL.Generator; public enum PackingRules { diff --git a/Ghost.Shader/Ghost.SDL.csproj b/Ghost.DSL/Ghost.DSL.csproj similarity index 100% rename from Ghost.Shader/Ghost.SDL.csproj rename to Ghost.DSL/Ghost.DSL.csproj diff --git a/Ghost.Shader/Compiler/SDLCompiler.cs b/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs similarity index 87% rename from Ghost.Shader/Compiler/SDLCompiler.cs rename to Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs index cb0a7f0..f7ba852 100644 --- a/Ghost.Shader/Compiler/SDLCompiler.cs +++ b/Ghost.DSL/ShaderCompiler/DSLShaderCompiler.cs @@ -1,11 +1,11 @@ using Ghost.Core; using Ghost.Core.Graphics; -using Ghost.SDL.Compiler.Parser; +using Ghost.DSL.ShaderCompiler.Parser; using System.Text; -namespace Ghost.SDL.Compiler; +namespace Ghost.DSL.ShaderCompiler; -public struct SDLError +public struct DSLShaderError { public string message; public int line; @@ -17,20 +17,20 @@ public struct SDLError } } -internal static class SDLCompiler +internal static class DSLShaderCompiler { private const string _GLOBAL_PROPERTY_FILE_NAME = "GlobalData.g.hlsl"; private const string _GENERATED_FILE_HEADER = "// Auto-generated shader file. Please do not edit this file directly."; // private struct ShaderInheritance // { - // public SDLSemantics? parent; + // public DSLShaderSemantics? parent; // public List? children; // } - public static List ParseShaders(TokenStream stream) + public static List ParseShaders(TokenStream stream) { - var shaders = new List(); + var shaders = new List(); while (stream.TryPeek(out var nextToken)) { @@ -52,13 +52,13 @@ internal static class SDLCompiler return shaders; } - public static SDLSemantics? SemanticAnalysis(SDLSyntax syntax, out List errors) + public static DSLShaderSemantics? SemanticAnalysis(DSLShaderSyntax syntax, out List errors) { - errors = new List(); + errors = new List(); if (string.IsNullOrWhiteSpace(syntax.name.lexeme)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = "Shader name cannot be empty.", line = syntax.name.line, @@ -72,11 +72,11 @@ internal static class SDLCompiler return shaderModel; } - private static List? TopologicalSort(ReadOnlySpan semantics) + private static List? TopologicalSort(ReadOnlySpan semantics) { var inDegrees = new Dictionary(); var childrenMap = new Dictionary>(); - var semanticsMap = new Dictionary(); + var semanticsMap = new Dictionary(); foreach (var s in semantics) { @@ -94,7 +94,7 @@ internal static class SDLCompiler } } - var queue = new Queue(); + var queue = new Queue(); foreach (var s in semantics) { if (inDegrees[s.name] == 0) @@ -103,7 +103,7 @@ internal static class SDLCompiler } } - var sortedList = new List(); + var sortedList = new List(); while (queue.Count > 0) { var current = queue.Dequeue(); @@ -123,7 +123,7 @@ internal static class SDLCompiler return sortedList.Count == semantics.Length ? sortedList : null; } - private static string GetPassUniqueId(SDLSemantics shader, PassSemantic pass) + private static string GetPassUniqueId(DSLShaderSemantics shader, PassSemantic pass) { return $"{shader.name}_{pass.name}"; } @@ -166,7 +166,7 @@ internal static class SDLCompiler // TODO: Implement shader inheritance resolution, including property and pass merging. // Currently, we just ignore inheritance. - public static ShaderDescriptor ResolveShader(SDLSemantics semantics) + public static ShaderDescriptor ResolveShader(DSLShaderSemantics semantics) { var descriptor = new ShaderDescriptor { @@ -209,7 +209,7 @@ internal static class SDLCompiler foreach (var pass in semantics.passes) { var localPipeline = MeragePipeline(pass.localPipeline, PipelineState.Default); - var fullPass = new FullPassDescriptor + var fullPass = new PassDescriptor { uniqueIdentifier = GetPassUniqueId(semantics, pass), name = pass.name, @@ -257,13 +257,21 @@ internal static class SDLCompiler return Result.Failure("Failed to generate global properties: " + globalPropResult.Message); } - var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory, globalPropResult.Value); + var generatedResult = GenerateShaderCode(desc, generatedOutputDirectory); if (generatedResult.IsFailure) { return Result.Failure("Failed to generate pass files: " + generatedResult.Message); } - desc.generatedCodePath = generatedResult.Value; + foreach (var pass in desc.passes) + { + if (pass is PassDescriptor fullPass) + { + fullPass.includes ??= new List(); + fullPass.includes.Add(globalPropResult.Value); + fullPass.includes.Add(generatedResult.Value); + } + } return desc; } @@ -304,7 +312,7 @@ internal static class SDLCompiler }; } - public static Result GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory, string globalDataPath) + public static Result GenerateShaderCode(ShaderDescriptor descriptor, string targetDirectory) { if (!Directory.Exists(targetDirectory)) { @@ -330,8 +338,7 @@ internal static class SDLCompiler #ifndef {fileDefine} #define {fileDefine} -#include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"" -#include ""{globalDataPath}"""); +#include ""F:/csharp/GhostEngine/Ghost.DSL/BuiltIn/Common.hlsl"""); sb.Append(@" struct PerMaterialData @@ -370,7 +377,7 @@ struct PerMaterialData #ifndef GLOBALDATA_G_HLSL #define GLOBALDATA_G_HLSL -#include ""F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl"" +#include ""F:/csharp/GhostEngine/Ghost.DSL/BuiltIn/Common.hlsl"" struct GlobalData {"); diff --git a/Ghost.Shader/Compiler/SDLSemantics.cs b/Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs similarity index 93% rename from Ghost.Shader/Compiler/SDLSemantics.cs rename to Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs index 5a2a0dd..b8235ec 100644 --- a/Ghost.Shader/Compiler/SDLSemantics.cs +++ b/Ghost.DSL/ShaderCompiler/DSLShaderSemantics.cs @@ -1,6 +1,6 @@ using Ghost.Core.Graphics; -namespace Ghost.SDL.Compiler; +namespace Ghost.DSL.ShaderCompiler; public enum PropertyScope { @@ -36,7 +36,7 @@ internal class PassSemantic public PipelineSemantic? localPipeline; } -internal class SDLSemantics +internal class DSLShaderSemantics { public string name = string.Empty; public string fallback = string.Empty; diff --git a/Ghost.Shader/Compiler/SDLSyntax.cs b/Ghost.DSL/ShaderCompiler/DSLShaderSyntax.cs similarity index 83% rename from Ghost.Shader/Compiler/SDLSyntax.cs rename to Ghost.DSL/ShaderCompiler/DSLShaderSyntax.cs index c067c5b..439027c 100644 --- a/Ghost.Shader/Compiler/SDLSyntax.cs +++ b/Ghost.DSL/ShaderCompiler/DSLShaderSyntax.cs @@ -1,4 +1,4 @@ -namespace Ghost.SDL.Compiler; +namespace Ghost.DSL.ShaderCompiler; internal struct FunctionCallDeclaration { @@ -20,6 +20,11 @@ internal struct ValueDeclaration public Token value; } +internal struct HlslDeclaration +{ + public List? tokens; +} + internal class PropertiesSyntax { public List? properties; @@ -36,12 +41,14 @@ internal class PassSyntax { public Token name; public PipelineSyntax? localPipeline; + public HlslDeclaration? hlsl; public List? defines; + public List? includes; public List? keywords; public List? functionCalls; } -internal class SDLSyntax +internal class DSLShaderSyntax { public Token name; public PropertiesSyntax? properties; diff --git a/Ghost.Shader/Compiler/Lexer.cs b/Ghost.DSL/ShaderCompiler/Lexer.cs similarity index 99% rename from Ghost.Shader/Compiler/Lexer.cs rename to Ghost.DSL/ShaderCompiler/Lexer.cs index 5bb9f99..3febff7 100644 --- a/Ghost.Shader/Compiler/Lexer.cs +++ b/Ghost.DSL/ShaderCompiler/Lexer.cs @@ -1,4 +1,4 @@ -namespace Ghost.SDL.Compiler; +namespace Ghost.DSL.ShaderCompiler; public class Lexer { diff --git a/Ghost.Shader/Compiler/Parser/DefinesBlock.cs b/Ghost.DSL/ShaderCompiler/Parser/DefinesBlock.cs similarity index 93% rename from Ghost.Shader/Compiler/Parser/DefinesBlock.cs rename to Ghost.DSL/ShaderCompiler/Parser/DefinesBlock.cs index 2c0be99..ff7e3cb 100644 --- a/Ghost.Shader/Compiler/Parser/DefinesBlock.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/DefinesBlock.cs @@ -1,4 +1,4 @@ -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; internal class DefinesBlock : IBlockParser, List> { @@ -27,7 +27,7 @@ internal class DefinesBlock : IBlockParser, List> return defines; } - public static List? SemanticAnalysis(List? syntax, List errors) + public static List? SemanticAnalysis(List? syntax, List errors) { if (syntax == null) { diff --git a/Ghost.Shader/Compiler/Parser/IBlockParser.cs b/Ghost.DSL/ShaderCompiler/Parser/IBlockParser.cs similarity index 54% rename from Ghost.Shader/Compiler/Parser/IBlockParser.cs rename to Ghost.DSL/ShaderCompiler/Parser/IBlockParser.cs index 76a8c55..597d294 100644 --- a/Ghost.Shader/Compiler/Parser/IBlockParser.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/IBlockParser.cs @@ -1,8 +1,8 @@ -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; internal interface IBlockParser { public static abstract bool ShouldEnter(Token token); public static abstract T? Parse(TokenStreamSlice ts); - public static abstract U? SemanticAnalysis(T? syntax, List errors); + public static abstract U? SemanticAnalysis(T? syntax, List errors); } diff --git a/Ghost.DSL/ShaderCompiler/Parser/IncludesBlock.cs b/Ghost.DSL/ShaderCompiler/Parser/IncludesBlock.cs new file mode 100644 index 0000000..4035bfd --- /dev/null +++ b/Ghost.DSL/ShaderCompiler/Parser/IncludesBlock.cs @@ -0,0 +1,59 @@ +namespace Ghost.DSL.ShaderCompiler.Parser; + +internal class IncludesBlock : IBlockParser, List> +{ + public static bool ShouldEnter(Token token) + { + return token.Match(TokenType.Keyword, TokenLexicon.KnownKeywords.INCLUDES); + } + + public static List Parse(TokenStreamSlice stream) + { + stream.Expect(TokenType.Keyword); + stream.Expect(TokenType.LBrace); + + var includes = new List(); + + var bodyStream = stream.Slice(stream.Remaining - 1); + while (bodyStream.HasMore) + { + var includeToken = bodyStream.Expect(TokenType.StringLiteral); + includes.Add(includeToken); + bodyStream.Expect(TokenType.Semicolon); + } + + stream.Expect(TokenType.RBrace); + + return includes; + } + + public static List? SemanticAnalysis(List? syntax, List errors) + { + if (syntax == null || syntax.Count == 0) + { + return null; + } + + var includes = new List(syntax.Count); + foreach (var includeToken in syntax) + { + var path = includeToken.lexeme; + if (File.Exists(path)) + { + includes.Add(path); + } + else + { + errors.Add(new DSLShaderError + { + message = $"Included file '{path}' not found.", + line = includeToken.line, + column = includeToken.column + }); + continue; + } + } + + return includes; + } +} diff --git a/Ghost.Shader/Compiler/Parser/KeywordsBlock.cs b/Ghost.DSL/ShaderCompiler/Parser/KeywordsBlock.cs similarity index 92% rename from Ghost.Shader/Compiler/Parser/KeywordsBlock.cs rename to Ghost.DSL/ShaderCompiler/Parser/KeywordsBlock.cs index cc6127c..e7a7b2e 100644 --- a/Ghost.Shader/Compiler/Parser/KeywordsBlock.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/KeywordsBlock.cs @@ -1,6 +1,6 @@ using Ghost.Core.Graphics; -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; internal class KeywordsBlock : IBlockParser, List> { @@ -30,7 +30,7 @@ internal class KeywordsBlock : IBlockParser, List< return keywords; } - public static List? SemanticAnalysis(List? syntax, List errors) + public static List? SemanticAnalysis(List? syntax, List errors) { if (syntax == null) { @@ -42,7 +42,7 @@ internal class KeywordsBlock : IBlockParser, List< { if (keyword.arguments == null || keyword.arguments.Count == 0) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Function '{keyword.name.lexeme}' must have at least one argument.", line = keyword.name.line, @@ -61,7 +61,7 @@ internal class KeywordsBlock : IBlockParser, List< group.space = KeywordSpace.Global; break; default: - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Unknown function name '{keyword.name.lexeme}'.", line = keyword.name.line, diff --git a/Ghost.Shader/Compiler/Parser/ParseUtility.cs b/Ghost.DSL/ShaderCompiler/Parser/ParseUtility.cs similarity index 97% rename from Ghost.Shader/Compiler/Parser/ParseUtility.cs rename to Ghost.DSL/ShaderCompiler/Parser/ParseUtility.cs index c2266d1..ac9edcb 100644 --- a/Ghost.Shader/Compiler/Parser/ParseUtility.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/ParseUtility.cs @@ -1,4 +1,4 @@ -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; internal static class ParseUtility { diff --git a/Ghost.Shader/Compiler/Parser/PassBlock.cs b/Ghost.DSL/ShaderCompiler/Parser/PassBlock.cs similarity index 89% rename from Ghost.Shader/Compiler/Parser/PassBlock.cs rename to Ghost.DSL/ShaderCompiler/Parser/PassBlock.cs index bfbc62b..a7c9481 100644 --- a/Ghost.Shader/Compiler/Parser/PassBlock.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/PassBlock.cs @@ -1,6 +1,6 @@ using Ghost.Core.Graphics; -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; // TODO: Add pass template support. // Pass templates let user to inject their own custom code into the generated HLSL code. @@ -35,6 +35,10 @@ internal class PassBlock : IBlockParser { pass.localPipeline = PipelineBlock.Parse(bodyStream.SliceNextBlock()); } + else if (IncludesBlock.ShouldEnter(nextToken)) + { + pass.includes = IncludesBlock.Parse(bodyStream.SliceNextBlock()); + } else if (nextToken.Match(TokenType.Identifier)) { var func = ParseUtility.ParseFunction(ref bodyStream, TokenType.StringLiteral); @@ -53,7 +57,7 @@ internal class PassBlock : IBlockParser return pass; } - public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List errors) + public static PassSemantic? SemanticAnalysis(PassSyntax? syntax, List errors) { if (syntax == null) { @@ -87,7 +91,7 @@ internal class PassBlock : IBlockParser break; default: - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Unknown function '{func.name.lexeme}' in pass {syntax.name.lexeme}.", line = func.name.line, @@ -102,7 +106,7 @@ internal class PassBlock : IBlockParser { // TODO: Inheritance from base pass. // TODO: Add mesh shader support. - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Pass {syntax.name.lexeme} must contain a mesh shader (ms) and a pixel shader (ps) declaration.", line = syntax.name.line, @@ -113,11 +117,11 @@ internal class PassBlock : IBlockParser return semantic; } - private static void AnalysisShaderEntry(List errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint) + private static void AnalysisShaderEntry(List errors, FunctionCallDeclaration func, ref ShaderEntryPoint shaderEntryPoint) { if (func.arguments?.Count != 2) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = "Shader declaration requires exactly two arguments: (shaderPath, entryPoint).", line = func.name.line, diff --git a/Ghost.Shader/Compiler/Parser/PipelineBlock.cs b/Ghost.DSL/ShaderCompiler/Parser/PipelineBlock.cs similarity index 92% rename from Ghost.Shader/Compiler/Parser/PipelineBlock.cs rename to Ghost.DSL/ShaderCompiler/Parser/PipelineBlock.cs index 6bdcd48..f9c3a72 100644 --- a/Ghost.Shader/Compiler/Parser/PipelineBlock.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/PipelineBlock.cs @@ -1,6 +1,6 @@ using Ghost.Core.Graphics; -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; internal class PipelineBlock : IBlockParser { @@ -38,7 +38,7 @@ internal class PipelineBlock : IBlockParser return pipeline; } - public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List errors) + public static PipelineSemantic? SemanticAnalysis(PipelineSyntax? syntax, List errors) { if (syntax == null) { @@ -59,7 +59,7 @@ internal class PipelineBlock : IBlockParser } else { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Invalid ZTest option: {valueDecl.value.lexeme}", line = valueDecl.value.line, @@ -75,7 +75,7 @@ internal class PipelineBlock : IBlockParser } else { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Invalid ZWrite option: {valueDecl.value.lexeme}", line = valueDecl.value.line, @@ -91,7 +91,7 @@ internal class PipelineBlock : IBlockParser } else { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Invalid Cull option: {valueDecl.value.lexeme}", line = valueDecl.value.line, @@ -107,7 +107,7 @@ internal class PipelineBlock : IBlockParser } else { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Invalid Blend option: {valueDecl.value.lexeme}", line = valueDecl.value.line, @@ -123,7 +123,7 @@ internal class PipelineBlock : IBlockParser } else { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Invalid Color Mask value: {valueDecl.value.lexeme}", line = valueDecl.value.line, diff --git a/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs b/Ghost.DSL/ShaderCompiler/Parser/PropertiesBlock.cs similarity index 91% rename from Ghost.Shader/Compiler/Parser/PropertiesBlock.cs rename to Ghost.DSL/ShaderCompiler/Parser/PropertiesBlock.cs index ba6d33b..1078309 100644 --- a/Ghost.Shader/Compiler/Parser/PropertiesBlock.cs +++ b/Ghost.DSL/ShaderCompiler/Parser/PropertiesBlock.cs @@ -2,11 +2,11 @@ using Ghost.Core.Graphics; using Misaki.HighPerformance.Mathematics; using System.Globalization; -namespace Ghost.SDL.Compiler.Parser; +namespace Ghost.DSL.ShaderCompiler.Parser; internal class PropertiesBlock : IBlockParser> { - private delegate object? PropertyValueBuilder(List tokens, List errors); + private delegate object? PropertyValueBuilder(List tokens, List errors); private sealed record PropTypeInfo(int ArgCount, TokenType ArgTokenType, PropertyValueBuilder? Builder); @@ -78,11 +78,11 @@ internal class PropertiesBlock : IBlockParser ParseTextureDefault(syntax[0], errors)), }; - private static float ParseFloatValue(Token token, List errors) + private static float ParseFloatValue(Token token, List errors) { if (!float.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Failed to parse float value '{token.lexeme}'.", line = token.line, @@ -93,11 +93,11 @@ internal class PropertiesBlock : IBlockParser errors) + private static int ParseIntValue(Token token, List errors) { if (!int.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Failed to parse int value '{token.lexeme}'.", line = token.line, @@ -108,11 +108,11 @@ internal class PropertiesBlock : IBlockParser errors) + private static uint ParseUIntValue(Token token, List errors) { if (!uint.TryParse(token.lexeme, CultureInfo.InvariantCulture, out var result)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Failed to parse uint value '{token.lexeme}'.", line = token.line, @@ -123,11 +123,11 @@ internal class PropertiesBlock : IBlockParser errors) + private static bool ParseBoolValue(Token token, List errors) { if (!bool.TryParse(token.lexeme, out var result)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Failed to parse bool value '{token.lexeme}'.", line = token.line, @@ -138,11 +138,11 @@ internal class PropertiesBlock : IBlockParser errors) + private static string ParseTextureDefault(Token token, List errors) { if (!TokenLexicon.IsTextureDefaultValue(token.lexeme)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Texture default value '{token.lexeme}' is not valid.", line = token.line, @@ -246,7 +246,7 @@ internal class PropertiesBlock : IBlockParser? SemanticAnalysis(PropertiesSyntax? syntax, List errors) + public static List? SemanticAnalysis(PropertiesSyntax? syntax, List errors) { if (syntax == null) { @@ -299,11 +299,11 @@ internal class PropertiesBlock : IBlockParser errors, PropertyDeclaration property, PropertySemantic model) + private static bool ValidatePropertyType(List errors, PropertyDeclaration property, PropertySemantic model) { if (!TokenLexicon.IsType(property.type.lexeme)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Shader property type '{property.type.lexeme}' is not a valid type.", line = property.type.line, @@ -317,11 +317,11 @@ internal class PropertiesBlock : IBlockParser errors, HashSet usedPropertyNames, PropertyDeclaration property, PropertySemantic model) + private static bool ValidatePropertyName(List errors, HashSet usedPropertyNames, PropertyDeclaration property, PropertySemantic model) { if (string.IsNullOrWhiteSpace(property.name.lexeme)) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = "Shader property has an empty name.", line = property.name.line, @@ -332,7 +332,7 @@ internal class PropertiesBlock : IBlockParser errors, PropertyDeclaration property, PropertySemantic model) + private static bool ValidatePropertyConstructor(List errors, PropertyDeclaration property, PropertySemantic model) { var constructor = property.propertyConstructor; if (!constructor.HasValue) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = "Shader property constructor is null.", line = property.name.line, @@ -364,7 +364,7 @@ internal class PropertiesBlock : IBlockParser +internal class ShaderBlock : IBlockParser { public static bool ShouldEnter(Token token) { return token.Match(TokenType.Keyword, TokenLexicon.KnownKeywords.SHADER); } - public static SDLSyntax Parse(TokenStreamSlice stream) + public static DSLShaderSyntax Parse(TokenStreamSlice stream) { - var shader = new SDLSyntax(); + var shader = new DSLShaderSyntax(); stream.Expect(TokenType.Keyword); shader.name = stream.Expect(TokenType.StringLiteral); @@ -49,14 +49,14 @@ internal class ShaderBlock : IBlockParser return shader; } - public static SDLSemantics? SemanticAnalysis(SDLSyntax? syntax, List errors) + public static DSLShaderSemantics? SemanticAnalysis(DSLShaderSyntax? syntax, List errors) { if (syntax == null) { return null; } - var shaderModel = new SDLSemantics + var shaderModel = new DSLShaderSemantics { name = syntax.name.lexeme, properties = PropertiesBlock.SemanticAnalysis(syntax.properties, errors), @@ -85,7 +85,7 @@ internal class ShaderBlock : IBlockParser case TokenLexicon.KnownFunctions.FALLBACK: if (func.arguments == null || func.arguments.Count != 1) { - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = "Fallback declaration requires exactly one arguments: (fallback shader name).", line = func.name.line, @@ -98,7 +98,7 @@ internal class ShaderBlock : IBlockParser shaderModel.fallback = func.arguments[0].lexeme; break; default: - errors.Add(new SDLError + errors.Add(new DSLShaderError { message = $"Unknown function '{func.name.lexeme}' in shader.", line = func.name.line, diff --git a/Ghost.Shader/Compiler/Token.cs b/Ghost.DSL/ShaderCompiler/Token.cs similarity index 99% rename from Ghost.Shader/Compiler/Token.cs rename to Ghost.DSL/ShaderCompiler/Token.cs index a598f0c..66485fc 100644 --- a/Ghost.Shader/Compiler/Token.cs +++ b/Ghost.DSL/ShaderCompiler/Token.cs @@ -1,4 +1,4 @@ -namespace Ghost.SDL.Compiler; +namespace Ghost.DSL.ShaderCompiler; [Flags] public enum TokenType diff --git a/Ghost.Shader/Compiler/TokenStream.cs b/Ghost.DSL/ShaderCompiler/TokenStream.cs similarity index 99% rename from Ghost.Shader/Compiler/TokenStream.cs rename to Ghost.DSL/ShaderCompiler/TokenStream.cs index b207699..9025b43 100644 --- a/Ghost.Shader/Compiler/TokenStream.cs +++ b/Ghost.DSL/ShaderCompiler/TokenStream.cs @@ -1,4 +1,4 @@ -namespace Ghost.SDL.Compiler; +namespace Ghost.DSL.ShaderCompiler; internal static class TokenStreamImple { diff --git a/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs b/Ghost.Editor.Core/Controls/BasicInput/Float3Field.cs similarity index 80% rename from Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs rename to Ghost.Editor.Core/Controls/BasicInput/Float3Field.cs index d7fd50d..ba46278 100644 --- a/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.cs +++ b/Ghost.Editor.Core/Controls/BasicInput/Float3Field.cs @@ -1,24 +1,24 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -using System.Numerics; +using Misaki.HighPerformance.Mathematics; namespace Ghost.Editor.Core.Controls; [TemplatePart(Name = "XComponent", Type = typeof(NumberBox))] [TemplatePart(Name = "YComponent", Type = typeof(NumberBox))] [TemplatePart(Name = "ZComponent", Type = typeof(NumberBox))] -public sealed partial class Vector3Field : ValueControl +public sealed partial class Float3Field : ValueControl { private NumberBox? _xComponent; private NumberBox? _yComponent; private NumberBox? _zComponent; - public Vector3Field() + public Float3Field() { - DefaultStyleKey = typeof(Vector3Field); + DefaultStyleKey = typeof(Float3Field); } - protected override void ValueChanged(Vector3 oldValue, Vector3 newValue) + protected override void ValueChanged(float3 oldValue, float3 newValue) { SyncFromValue(); } @@ -45,9 +45,9 @@ public sealed partial class Vector3Field : ValueControl private void SyncFromValue() { SuppressChangedEvent = true; - _xComponent?.Value = Value.X; - _yComponent?.Value = Value.Y; - _zComponent?.Value = Value.Z; + _xComponent?.Value = Value.x; + _yComponent?.Value = Value.y; + _zComponent?.Value = Value.z; SuppressChangedEvent = false; } @@ -58,7 +58,7 @@ public sealed partial class Vector3Field : ValueControl return; } - var newValue = new Vector3( + var newValue = new float3( (float)(_xComponent?.Value ?? 0), (float)(_yComponent?.Value ?? 0), (float)(_zComponent?.Value ?? 0)); @@ -66,4 +66,4 @@ public sealed partial class Vector3Field : ValueControl RiseChangedEvent(Value, newValue); Value = newValue; } -} \ No newline at end of file +} diff --git a/Ghost.Editor.Core/Controls/BasicInput/Vector3Field.xaml b/Ghost.Editor.Core/Controls/BasicInput/Float3Field.xaml similarity index 100% rename from Ghost.Editor.Core/Controls/BasicInput/Vector3Field.xaml rename to Ghost.Editor.Core/Controls/BasicInput/Float3Field.xaml diff --git a/Ghost.Editor.Core/Ghost.Editor.Core.csproj b/Ghost.Editor.Core/Ghost.Editor.Core.csproj index af107cd..4dc15b0 100644 --- a/Ghost.Editor.Core/Ghost.Editor.Core.csproj +++ b/Ghost.Editor.Core/Ghost.Editor.Core.csproj @@ -9,7 +9,8 @@ 10.0.20348.0 enable True - preview + + preview @@ -41,4 +42,4 @@ Designer - \ No newline at end of file + diff --git a/Ghost.Editor.Core/SceneGraph/EditorWorldManager.cs b/Ghost.Editor.Core/SceneGraph/EditorSceneManager.cs similarity index 77% rename from Ghost.Editor.Core/SceneGraph/EditorWorldManager.cs rename to Ghost.Editor.Core/SceneGraph/EditorSceneManager.cs index 5ac6522..35f1b1e 100644 --- a/Ghost.Editor.Core/SceneGraph/EditorWorldManager.cs +++ b/Ghost.Editor.Core/SceneGraph/EditorSceneManager.cs @@ -12,16 +12,16 @@ public enum OpenWorldMode AdditiveWithoutLoading } -public static class EditorWorldManager +public static class EditorSceneManager { // TODO: Use guid keys instead of string paths for better performance and uniqueness - private static readonly Dictionary s_loadedWorlds = new(); - public static IEnumerable LoadedWorlds => s_loadedWorlds.Values; + private static readonly Dictionary s_loadedWorlds = new(); + public static IEnumerable LoadedWorlds => s_loadedWorlds.Values; - public static event Action? OnWorldLoaded; - public static event Action? OnWorldUnloaded; + public static event Action? OnWorldLoaded; + public static event Action? OnWorldUnloaded; - public static async Task LoadWorld(string worldPath) + public static async Task LoadSceneAsync(string worldPath) { if (s_loadedWorlds.ContainsKey(worldPath) || !File.Exists(worldPath) @@ -40,7 +40,7 @@ public static class EditorWorldManager } await using var readStream = new FileStream(worldPath, FileMode.Open, FileAccess.Read, FileShare.Read); - var deserializedScene = await JsonSerializer.DeserializeAsync(readStream, Engine.Resources.EngineResource.defaultSerializerOptions) ?? throw new Exception("Deserialization failed."); + var deserializedScene = await JsonSerializer.DeserializeAsync(readStream, Engine.Resources.EngineResource.defaultSerializerOptions) ?? throw new Exception("Deserialization failed."); s_loadedWorlds.Clear(); @@ -50,4 +50,4 @@ public static class EditorWorldManager progressService.HideProgress(); OnWorldLoaded?.Invoke(deserializedScene); } -} \ No newline at end of file +} diff --git a/Ghost.Editor.Core/SceneGraph/EntityNode.cs b/Ghost.Editor.Core/SceneGraph/EntityNode.cs index e6c3608..cefe95c 100644 --- a/Ghost.Editor.Core/SceneGraph/EntityNode.cs +++ b/Ghost.Editor.Core/SceneGraph/EntityNode.cs @@ -13,7 +13,7 @@ namespace Ghost.Editor.Core.SceneGraph; public partial class EntityNode : SceneGraphNode { - public WorldNode Owner + public SceneNode Owner { get; set; @@ -26,7 +26,7 @@ public partial class EntityNode : SceneGraphNode public override SceneGraphNodeType NodeType => SceneGraphNodeType.Entity; - public EntityNode(WorldNode owner, Entity entity, string name) + public EntityNode(SceneNode owner, Entity entity, string name) { Owner = owner; Entity = entity; @@ -80,7 +80,7 @@ public partial class EntityNode : IInspectable { get { - var r = Owner.World.EntityManager.GetEntityLocation(Entity); + var r = Owner.Scene.World.EntityManager.GetEntityLocation(Entity); if (!r) { return null; @@ -93,7 +93,7 @@ public partial class EntityNode : IInspectable }; var location = r.Value; - ref var archetype = ref Owner.World.GetArchetypeReference(location.archetypeID); + ref var archetype = ref Owner.Scene.World.ComponentManager.GetArchetypeReference(location.archetypeID); var it = archetype._signature.GetIterator(); while (it.Next(out var typeID)) @@ -114,11 +114,11 @@ public partial class EntityNode : IInspectable continue; } - var componentView = new ComponentView(t.Name, Owner.World, Entity, t); + var componentView = new ComponentView(t.Name, Owner.Scene.World, Entity, t); root.Children.Add(componentView); } return root; } } -} \ No newline at end of file +} diff --git a/Ghost.Editor.Core/SceneGraph/SceneGraphHelpers.cs b/Ghost.Editor.Core/SceneGraph/SceneGraphHelpers.cs index 8263b1f..e9b1a44 100644 --- a/Ghost.Editor.Core/SceneGraph/SceneGraphHelpers.cs +++ b/Ghost.Editor.Core/SceneGraph/SceneGraphHelpers.cs @@ -10,10 +10,10 @@ public class SceneGraphHelpers /// /// The world context where the entity will be created. /// The entity to be wrapped in the . - public static EntityNode CreateEntityNode(WorldNode owner, Entity entity, string name) + public static EntityNode CreateEntityNode(SceneNode owner, Entity entity, string name) { - owner.World.EntityManager.AddComponent(entity, new LocalToWorld { matrix = Misaki.HighPerformance.Mathematics.float4x4.identity }); - owner.World.EntityManager.AddComponent(entity, Hierarchy.Root); + owner.Scene.World.EntityManager.AddComponent(entity, new LocalToWorld { matrix = Misaki.HighPerformance.Mathematics.float4x4.identity }); + owner.Scene.World.EntityManager.AddComponent(entity, Hierarchy.Root); return new EntityNode(owner, entity, name); } @@ -21,9 +21,9 @@ public class SceneGraphHelpers /// Creates a new and entity with default components. /// /// The world context where the entity will be created. - public static EntityNode CreateEntityNode(WorldNode owner, string name) + public static EntityNode CreateEntityNode(SceneNode owner, string name) { - var entity = owner.World.EntityManager.CreateEntity(); + var entity = owner.Scene.World.EntityManager.CreateEntity(); return CreateEntityNode(owner, entity, name); } @@ -33,10 +33,10 @@ public class SceneGraphHelpers /// The world context where the entities exist. /// The parent entity to which the child will be attached. /// The child entity to be attached. - public static void AttachChild(WorldNode scene, EntityNode parentNode, EntityNode childNode) + public static void AttachChild(SceneNode scene, EntityNode parentNode, EntityNode childNode) { // 1) If the child already has a parent, detach it first - var childHierarchy = scene.World.EntityManager.GetComponent(childNode.Entity); + var childHierarchy = scene.Scene.World.EntityManager.GetComponent(childNode.Entity); if (childHierarchy.parent != Entity.Invalid) { DetachFromParent(scene, childNode); @@ -46,14 +46,14 @@ public class SceneGraphHelpers childHierarchy.parent = parentNode.Entity; // 3) Insert child at the head of parent's child list - var parentHierarchy = scene.World.EntityManager.GetComponent(parentNode.Entity); + var parentHierarchy = scene.Scene.World.EntityManager.GetComponent(parentNode.Entity); childHierarchy.nextSibling = parentHierarchy.firstChild; parentHierarchy.firstChild = childNode.Entity; // 4) Write back - scene.World.EntityManager.SetComponent(parentNode.Entity, parentHierarchy); - scene.World.EntityManager.SetComponent(childNode.Entity, childHierarchy); + scene.Scene.World.EntityManager.SetComponent(parentNode.Entity, parentHierarchy); + scene.Scene.World.EntityManager.SetComponent(childNode.Entity, childHierarchy); // 5) Update children list in parent node parentNode.AddChild(childNode); @@ -64,16 +64,16 @@ public class SceneGraphHelpers /// /// The world context where the entities exist. /// The entity to detach from its parent. - public static void DetachFromParent(WorldNode scene, EntityNode node) + public static void DetachFromParent(SceneNode scene, EntityNode node) { - var hierarchy = scene.World.EntityManager.GetComponent(node.Entity); + var hierarchy = scene.Scene.World.EntityManager.GetComponent(node.Entity); var parent = hierarchy.parent; if (parent == Entity.Invalid) { return; // already root } - var parentHierarchy = scene.World.EntityManager.GetComponent(parent); + var parentHierarchy = scene.Scene.World.EntityManager.GetComponent(parent); // If entity is the first child, simply move head if (parentHierarchy.firstChild == node.Entity) @@ -86,11 +86,11 @@ public class SceneGraphHelpers var prevSibling = parentHierarchy.firstChild; while (prevSibling != Entity.Invalid) { - var prevHierarchy = scene.World.EntityManager.GetComponent(prevSibling); + var prevHierarchy = scene.Scene.World.EntityManager.GetComponent(prevSibling); if (prevHierarchy.nextSibling == node.Entity) { prevHierarchy.nextSibling = hierarchy.nextSibling; - scene.World.EntityManager.SetComponent(prevSibling, prevHierarchy); + scene.Scene.World.EntityManager.SetComponent(prevSibling, prevHierarchy); break; } @@ -103,10 +103,10 @@ public class SceneGraphHelpers hierarchy.nextSibling = Entity.Invalid; // Write back - scene.World.EntityManager.SetComponent(parent, parentHierarchy); - scene.World.EntityManager.SetComponent(node.Entity, hierarchy); + scene.Scene.World.EntityManager.SetComponent(parent, parentHierarchy); + scene.Scene.World.EntityManager.SetComponent(node.Entity, hierarchy); // Remove from parent's children list scene.EntityNodeLookup[parent].RemoveChild(node); } -} \ No newline at end of file +} diff --git a/Ghost.Editor.Core/SceneGraph/SceneNode.cs b/Ghost.Editor.Core/SceneGraph/SceneNode.cs index 2a1f5e5..68a1413 100644 --- a/Ghost.Editor.Core/SceneGraph/SceneNode.cs +++ b/Ghost.Editor.Core/SceneGraph/SceneNode.cs @@ -1,6 +1,183 @@ +using Ghost.Editor.Core.AssetHandle; +using Ghost.Editor.Core.Inspector; +using Ghost.Editor.Core.Resources; +using Ghost.Editor.Core.Serializer; +using Ghost.Engine.Components; +using Ghost.Engine.Core; +using Ghost.Engine.IO; +using Ghost.Entities; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + namespace Ghost.Editor.Core.SceneGraph; -public class SceneNode : SceneGraphNode +[CustomSerializer(typeof(SceneNodeSerializer))] +public partial class SceneNode : SceneGraphNode, IEquatable { + private Scene _scene; + private Dictionary _entityNodeLookup = new(); + + public Scene Scene => _scene; + public Dictionary EntityNodeLookup => _entityNodeLookup; + public override SceneGraphNodeType NodeType => SceneGraphNodeType.Scene; + + public SceneNode(Scene scene, string name) + { + _scene = scene; + Name = name; + } + + private void UpdateLookup(Entity key, EntityNode value) + { + _entityNodeLookup[key] = value; + if (value.Children == null) + { + return; + } + + foreach (var child in value.Children) + { + if (child is EntityNode entityChild) + { + UpdateLookup(entityChild.Entity, entityChild); + } + } + } + + public override void AddChild(SceneGraphNode child) + { + if (child is not EntityNode entityNode) + { + throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); + } + + base.AddChild(entityNode); + UpdateLookup(entityNode.Entity, entityNode); + } + + public override bool RemoveChild(SceneGraphNode child) + { + if (child is not EntityNode entityNode) + { + throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); + } + + var result = base.RemoveChild(child); + if (result) + { + _entityNodeLookup.Remove(entityNode.Entity); + } + + return result; + } + + private EntityNode BuildNodeRecursive(Entity entity) + { + if (!_entityNodeLookup.TryGetValue(entity, out var node)) + { + node = new EntityNode(this, entity, "New Entity"); + _entityNodeLookup[entity] = node; + } + + var hc = _scene.World.EntityManager.GetComponent(entity); + var child = hc.firstChild; + + while (child != Entity.Invalid) + { + node.AddChild(BuildNodeRecursive(child)); + var childHC = _scene.World.EntityManager.GetComponent(child); + child = childHC.nextSibling; + } + + return node; + } + + private void BuildGraph() + { + var queryID = new QueryBuilder() + .WithAll() + .Build(_scene.World); + + _scene.World.ComponentManager.GetEntityQueryReference(queryID).ForEach((entity, ref hierarchy) => + { + if (hierarchy.parent == Entity.Invalid) + { + var node = BuildNodeRecursive(entity); + AddChild(node); + } + }); + } + + public Task LoadAsync() + { + return Task.Run(BuildGraph); + } + + public void Unload() + { + _scene = null!; + + Children?.Clear(); + _entityNodeLookup.Clear(); + } + + public override string ToString() + { + return $"WorldNode: {Name} (World ID: {_scene.ID})"; + } + + public override int GetHashCode() + { + return HashCode.Combine(_scene, Name); + } + + public override bool Equals(object? obj) + { + return obj is SceneNode other && Equals(other); + } + + public bool Equals(SceneNode? other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return _scene.Equals(other._scene) && Name == other.Name; + } + + public static bool operator ==(SceneNode? left, SceneNode? right) + { + if (left is null) + { + return right is null; + } + return left.Equals(right); + } + + public static bool operator !=(SceneNode? left, SceneNode? right) + { + return !(left == right); + } +} + +public partial class SceneNode : IInspectable +{ + public IconSource? Icon => EditorIconSource.scene_24; + + [AssetOpenHandler(FileExtensions.SCENE_FILE_EXTENSION)] + public static async void Open(string path) + { + await EditorSceneManager.LoadSceneAsync(path); + } + + public UIElement? HeaderContent => null; + + public UIElement? InspectorContent => null; } diff --git a/Ghost.Editor.Core/SceneGraph/WorldNode.cs b/Ghost.Editor.Core/SceneGraph/WorldNode.cs deleted file mode 100644 index c15ffac..0000000 --- a/Ghost.Editor.Core/SceneGraph/WorldNode.cs +++ /dev/null @@ -1,183 +0,0 @@ -using Ghost.Editor.Core.AssetHandle; -using Ghost.Editor.Core.Inspector; -using Ghost.Editor.Core.Resources; -using Ghost.Editor.Core.Serializer; -using Ghost.Engine.Components; -using Ghost.Engine.IO; -using Ghost.Entities; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -namespace Ghost.Editor.Core.SceneGraph; - -// FIX: This should be scene node, not world node -[CustomSerializer(typeof(WorldNodeSerializer))] -public partial class WorldNode : SceneGraphNode, IEquatable -{ - private World _world; - private Dictionary _entityNodeLookup = new(); - - public World World => _world; - public Dictionary EntityNodeLookup => _entityNodeLookup; - - public override SceneGraphNodeType NodeType => SceneGraphNodeType.Scene; - - public WorldNode(World world, string name) - { - _world = world; - Name = name; - } - - private void UpdateLookup(Entity key, EntityNode value) - { - _entityNodeLookup[key] = value; - if (value.Children == null) - { - return; - } - - foreach (var child in value.Children) - { - if (child is EntityNode entityChild) - { - UpdateLookup(entityChild.Entity, entityChild); - } - } - } - - public override void AddChild(SceneGraphNode child) - { - if (child is not EntityNode entityNode) - { - throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); - } - - base.AddChild(entityNode); - UpdateLookup(entityNode.Entity, entityNode); - } - - public override bool RemoveChild(SceneGraphNode child) - { - if (child is not EntityNode entityNode) - { - throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); - } - - var result = base.RemoveChild(child); - if (result) - { - _entityNodeLookup.Remove(entityNode.Entity); - } - - return result; - } - - private EntityNode BuildNodeRecursive(Entity entity) - { - if (!_entityNodeLookup.TryGetValue(entity, out var node)) - { - node = new EntityNode(this, entity, "New Entity"); - _entityNodeLookup[entity] = node; - } - - var hc = _world.EntityManager.GetComponent(entity); - var child = hc.firstChild; - - while (child != Entity.Invalid) - { - node.AddChild(BuildNodeRecursive(child)); - var childHC = _world.EntityManager.GetComponent(child); - child = childHC.nextSibling; - } - - return node; - } - - private void BuildGraph() - { - var queryID = new QueryBuilder() - .WithAll() - .Build(_world); - - _world.GetEntityQueryReference(queryID).ForEach((entity, ref hierarchy) => - { - if (hierarchy.parent == Entity.Invalid) - { - var node = BuildNodeRecursive(entity); - AddChild(node); - } - }); - } - - public Task LoadAsync() - { - return Task.Run(BuildGraph); - } - - public void Unload() - { - _world = null!; - - Children?.Clear(); - _entityNodeLookup.Clear(); - } - - public override string ToString() - { - return $"WorldNode: {Name} (World ID: {_world.ID})"; - } - - public override int GetHashCode() - { - return HashCode.Combine(_world, Name); - } - - public override bool Equals(object? obj) - { - return obj is WorldNode other && Equals(other); - } - - public bool Equals(WorldNode? other) - { - if (other is null) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - return _world.Equals(other._world) && Name == other.Name; - } - - public static bool operator ==(WorldNode? left, WorldNode? right) - { - if (left is null) - { - return right is null; - } - return left.Equals(right); - } - - public static bool operator !=(WorldNode? left, WorldNode? right) - { - return !(left == right); - } -} - -public partial class WorldNode : IInspectable -{ - public IconSource? Icon => EditorIconSource.scene_24; - - [AssetOpenHandler(FileExtensions.SCENE_FILE_EXTENSION)] - public static async void Open(string path) - { - await EditorWorldManager.LoadWorld(path); - } - - public UIElement? HeaderContent => null; - - public UIElement? InspectorContent => null; -} \ No newline at end of file diff --git a/Ghost.Editor.Core/Serializer/WorldNodeSerializer.cs b/Ghost.Editor.Core/Serializer/SceneNodeSerializer.cs similarity index 87% rename from Ghost.Editor.Core/Serializer/WorldNodeSerializer.cs rename to Ghost.Editor.Core/Serializer/SceneNodeSerializer.cs index 3fe1107..4e97ecc 100644 --- a/Ghost.Editor.Core/Serializer/WorldNodeSerializer.cs +++ b/Ghost.Editor.Core/Serializer/SceneNodeSerializer.cs @@ -1,5 +1,4 @@ using Ghost.Editor.Core.SceneGraph; -using Ghost.Engine; using Ghost.Engine.IO; using Ghost.Engine.Utilities; using Ghost.Entities; @@ -7,7 +6,7 @@ using System.Text.Json; namespace Ghost.Editor.Core.Serializer; -internal class WorldNodeSerializer : CustomSerializer +internal class SceneNodeSerializer : CustomSerializer { private static class Property { @@ -22,19 +21,19 @@ internal class WorldNodeSerializer : CustomSerializer public override bool CanConvert(Type typeToConvert) { - return typeToConvert == typeof(WorldNode) || typeToConvert.IsSubclassOf(typeof(WorldNode)); + return typeToConvert == typeof(SceneNode) || typeToConvert.IsSubclassOf(typeof(SceneNode)); } - public unsafe override void SerializeJson(Utf8JsonWriter writer, WorldNode value, JsonSerializerOptions options) + public unsafe override void SerializeJson(Utf8JsonWriter writer, SceneNode value, JsonSerializerOptions options) { writer.WriteObject(() => { writer.WriteString(Property.NAME, value.Name); writer.WriteStartArray(Property.ENTITIES); - for (var i = 0; i < value.World.ArchetypeCount; i++) + for (var i = 0; i < value.Scene.World.ComponentManager.ArchetypeCount; i++) { - ref var archetype = ref value.World.GetArchetypeReference(i); + ref var archetype = ref value.Scene.World.ComponentManager.GetArchetypeReference(i); for (var j = 0; j < archetype.ChunkCount; j++) { @@ -64,7 +63,7 @@ internal class WorldNodeSerializer : CustomSerializer writer.WriteEndArray(); - writer.WriteArray(Property.SYSTEMS, value.World.SystemManager.Systems, system => + writer.WriteArray(Property.SYSTEMS, value.Scene.World.SystemManager.Systems, system => { var name = system.GetType().AssemblyQualifiedName; if (name == null) @@ -77,7 +76,7 @@ internal class WorldNodeSerializer : CustomSerializer }); } - public override WorldNode? DeserializeJson(ref Utf8JsonReader reader, JsonSerializerOptions options) + public override SceneNode? DeserializeJson(ref Utf8JsonReader reader, JsonSerializerOptions options) { throw new NotImplementedException(); @@ -137,12 +136,12 @@ internal class WorldNodeSerializer : CustomSerializer //return result; } - public override void SerializeBinary(BinaryWriter writer, WorldNode value) + public override void SerializeBinary(BinaryWriter writer, SceneNode value) { throw new NotImplementedException(); } - public override WorldNode? DeserializeBinary(BinaryReader reader) + public override SceneNode? DeserializeBinary(BinaryReader reader) { throw new NotImplementedException(); } diff --git a/Ghost.Editor/Components/LocalToWorldEditor.cs b/Ghost.Editor/Components/LocalToWorldEditor.cs index 2c38b3b..0cfbf59 100644 --- a/Ghost.Editor/Components/LocalToWorldEditor.cs +++ b/Ghost.Editor/Components/LocalToWorldEditor.cs @@ -3,41 +3,45 @@ using Ghost.Editor.Core.Inspector; using Ghost.Engine.Components; using Ghost.Engine.Utilities; using Microsoft.UI.Xaml.Controls; +using Misaki.HighPerformance.Mathematics; namespace Ghost.Editor.Components; [CustomEditor(typeof(LocalToWorld))] internal class LocalToWorldEditor : ComponentEditor { - private Vector3Field _translationField = null!; - private Vector3Field _rotationField = null!; - private Vector3Field _scaleField = null!; + private Float3Field _translationField = null!; + private Float3Field _rotationField = null!; + private Float3Field _scaleField = null!; public override void Create(StackPanel container) { - _translationField = new Vector3Field(); - _rotationField = new Vector3Field(); - _scaleField = new Vector3Field(); + _translationField = new Float3Field(); + _rotationField = new Float3Field(); + _scaleField = new Float3Field(); _translationField.OnValueChanged += (s, e) => { - var data = ComponentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var _, out var oldRotation, out var oldScale); - data.ValueRW.matrix = MatrixUtility.CreateTRS(e.NewValue, oldRotation, oldScale); + ref var data = ref ComponentObject.GetData(); + data.matrix.c3.xyz = e.NewValue; }; _rotationField.OnValueChanged += (s, e) => { - var data = ComponentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var _, out var oldScale); - data.ValueRW.matrix = MatrixUtility.CreateTRS(oldTranslation, e.NewValue.ToQuaternion(), oldScale); + ref var data = ref ComponentObject.GetData(); + var newRotation = quaternion.EulerXYZ(e.NewValue * math.TORADIANS); + + data.matrix.GetTRS(out var oldTranslation, out var _, out var oldScale); + data.matrix = float4x4.TRS(oldTranslation, newRotation, oldScale); }; _scaleField.OnValueChanged += (s, e) => { - var data = ComponentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var oldTranslation, out var oldRotation, out var _); - data.ValueRW.matrix = MatrixUtility.CreateTRS(oldTranslation, oldRotation, e.NewValue); + ref var data = ref ComponentObject.GetData(); + var newScale = e.NewValue; + + data.matrix.GetTRS(out var oldTranslation, out var oldRotation, out var _); + data.matrix = float4x4.TRS(oldTranslation, oldRotation, newScale); }; container.Children.Add(new PropertyField() { Label = "Position", Content = _translationField }); @@ -48,14 +52,14 @@ internal class LocalToWorldEditor : ComponentEditor public override void Update() { var data = ComponentObject.GetData(); - MatrixUtility.GetTRS(data.ValueRO.matrix, out var translation, out var rotation, out var scale); + data.matrix.GetTRS(out var position, out var rotation, out var scale); - _translationField.Value = translation; - _rotationField.Value = VectorUtility.CreateFromQuaternion(rotation); + _translationField.Value = position; + _rotationField.Value = math.degrees(math.EulerXYZ(rotation)); _scaleField.Value = scale; } public override void Destroy() { } -} \ No newline at end of file +} diff --git a/Ghost.Editor/Ghost.Editor.csproj b/Ghost.Editor/Ghost.Editor.csproj index 5bd589e..9b55d34 100644 --- a/Ghost.Editor/Ghost.Editor.csproj +++ b/Ghost.Editor/Ghost.Editor.csproj @@ -8,6 +8,8 @@ win-$(Platform).pubxml true true + + preview @@ -117,4 +119,4 @@ Ghost.Editor enable - \ No newline at end of file + diff --git a/Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs b/Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs index 06c246e..36e7165 100644 --- a/Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs +++ b/Ghost.Editor/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs @@ -8,31 +8,31 @@ namespace Ghost.Editor.ViewModels.Pages.EngineEditor; internal partial class HierarchyViewModel : ObservableObject, INavigationAware { [ObservableProperty] - public partial ObservableCollection SceneList + public partial ObservableCollection SceneList { get; private set; - } = new(EditorWorldManager.LoadedWorlds); + } = new(EditorSceneManager.LoadedWorlds); - private void OnWorldLoaded(WorldNode node) + private void OnWorldLoaded(SceneNode node) { SceneList.Add(node); } - private void OnWorldUnloaded(WorldNode node) + private void OnWorldUnloaded(SceneNode node) { SceneList.Remove(node); } public void OnNavigatedTo(object? parameter) { - EditorWorldManager.OnWorldLoaded += OnWorldLoaded; - EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded; + EditorSceneManager.OnWorldLoaded += OnWorldLoaded; + EditorSceneManager.OnWorldUnloaded += OnWorldUnloaded; } public void OnNavigatedFrom() { - EditorWorldManager.OnWorldLoaded -= OnWorldLoaded; - EditorWorldManager.OnWorldUnloaded -= OnWorldUnloaded; + EditorSceneManager.OnWorldLoaded -= OnWorldLoaded; + EditorSceneManager.OnWorldUnloaded -= OnWorldUnloaded; } -} \ No newline at end of file +} diff --git a/Ghost.Engine/Components/SceneID.cs b/Ghost.Engine/Components/SceneID.cs new file mode 100644 index 0000000..bb59a70 --- /dev/null +++ b/Ghost.Engine/Components/SceneID.cs @@ -0,0 +1,8 @@ +using Ghost.Entities; + +namespace Ghost.Engine.Components; + +public struct SceneID : IComponent // TODO: ISharedComponent +{ + public short id; +} diff --git a/Ghost.Engine/Core/Scene.cs b/Ghost.Engine/Core/Scene.cs new file mode 100644 index 0000000..72a969e --- /dev/null +++ b/Ghost.Engine/Core/Scene.cs @@ -0,0 +1,41 @@ +using Ghost.Entities; + +namespace Ghost.Engine.Core; + +public partial class Scene +{ + private static short s_nextSceneID = 0; +} + +public partial class Scene : IDisposable +{ + private readonly World _world; + private readonly short _id; + + private bool _isDisposed; + + public World World => _world; + public short ID => _id; + + public Scene(World world) + { + _world = world; + _id = s_nextSceneID++; + } + + ~Scene() + { + Dispose(); + } + + public void Dispose() + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + GC.SuppressFinalize(this); + } +} diff --git a/Ghost.Engine/Utilities/MathUtility.cs b/Ghost.Engine/Utilities/MathUtility.cs index 83c1c77..5edce1a 100644 --- a/Ghost.Engine/Utilities/MathUtility.cs +++ b/Ghost.Engine/Utilities/MathUtility.cs @@ -1,31 +1,54 @@ +using Misaki.HighPerformance.Mathematics; using System.Runtime.CompilerServices; namespace Ghost.Engine.Utilities; public static class MathUtility { - public const float RAD_TO_DEG = 180f / MathF.PI; - public const float DEG_TO_RAD = MathF.PI / 180f; - - /// - /// Converts radians to degrees. - /// - /// The angle in radians to convert. - /// The angle in degrees. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float RadToDeg(float radians) + extension(float4x4 matrix) { - return radians * RAD_TO_DEG; - } + /// + /// Creates a transformation matrix from position, rotation, and scale vectors. + /// + /// Defines the translation component of the transformation matrix. + /// Specifies the orientation of the object in 3D space. + /// Determines the size of the object along each axis. + /// Returns a transformation matrix that combines the specified position, rotation, and scale. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float4x4 TRS(float3 position, quaternion rotation, float3 scale) + { + var R = new float3x3(rotation); + return new float4x4( + R[0][0] * scale.x, R[0][1] * scale.y, R[0][2] * scale.z, position.x, + R[1][0] * scale.x, R[1][1] * scale.y, R[1][2] * scale.z, position.y, + R[2][0] * scale.x, R[2][1] * scale.y, R[2][2] * scale.z, position.z, + 0f, 0f, 0f, 1f + ); + } - /// - /// Converts degrees to radians. - /// - /// The angle in degrees to convert. - /// The angle in radians. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DegToRad(float degrees) - { - return degrees * DEG_TO_RAD; + /// + /// Gets the translation, rotation, and scale components from a transformation matrix. + /// + /// The position component extracted from the matrix. + /// The rotation component extracted from the matrix as a quaternion. + /// The scale component extracted from the matrix. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void GetTRS(out float3 position, out quaternion rotation, out float3 scale) + { + position = matrix.c3.xyz; + + var scaleX = math.length(matrix.c0.xyz); + var scaleY = math.length(matrix.c1.xyz); + var scaleZ = math.length(matrix.c2.xyz); + scale = new float3(scaleX, scaleY, scaleZ); + + var rotationMatrix = new float3x3( + matrix.c0.x / scale.x, matrix.c0.y / scale.x, matrix.c0.z / scale.x, + matrix.c1.x / scale.y, matrix.c1.y / scale.y, matrix.c1.z / scale.y, + matrix.c2.x / scale.z, matrix.c2.y / scale.z, matrix.c2.z / scale.z + ); + + rotation = new quaternion(rotationMatrix); + } } -} \ No newline at end of file +} diff --git a/Ghost.Engine/Utilities/MatrixUtility.cs b/Ghost.Engine/Utilities/MatrixUtility.cs deleted file mode 100644 index 8ae13da..0000000 --- a/Ghost.Engine/Utilities/MatrixUtility.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Ghost.Engine.Utilities; - -public static class MatrixUtility -{ - /// - /// Generates a transformation matrix from position, rotation, and scale vectors. - /// - /// Defines the translation component of the transformation matrix. - /// Specifies the orientation of the object in 3D space. - /// Determines the size of the object along each axis. - /// Returns a transformation matrix that combines the specified position, rotation, and scale. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Matrix4x4 CreateTRS(Vector3 position, Quaternion rotation, Vector3 scale) - { - return Matrix4x4.CreateScale(scale) * Matrix4x4.CreateFromQuaternion(rotation) * Matrix4x4.CreateTranslation(position); - } - - /// - /// Decomposes a transformation matrix into its position, rotation, and scale components. - /// - /// This method assumes the input matrix represents a valid affine transformation, including - /// translation, rotation, and scaling. If the matrix contains skew or other non-standard transformations, the - /// results may be undefined. - /// The to decompose. Must represent a valid transformation matrix. - /// When the method returns, contains the position component extracted from the matrix. - /// When the method returns, contains the rotation component extracted from the matrix as a . - /// When the method returns, contains the scale component extracted from the matrix. - public static void GetTRS(Matrix4x4 matrix, out Vector3 position, out Quaternion rotation, out Vector3 scale) - { - position = new(matrix.M41, matrix.M42, matrix.M43); - - var scaleX = new Vector3(matrix.M11, matrix.M12, matrix.M13).Length(); - var scaleY = new Vector3(matrix.M21, matrix.M22, matrix.M23).Length(); - var scaleZ = new Vector3(matrix.M31, matrix.M32, matrix.M33).Length(); - scale = new(scaleX, scaleY, scaleZ); - - Matrix4x4 rotationMatrix = new( - matrix.M11 / scale.X, matrix.M12 / scale.X, matrix.M13 / scale.X, 0, - matrix.M21 / scale.Y, matrix.M22 / scale.Y, matrix.M23 / scale.Y, 0, - matrix.M31 / scale.Z, matrix.M32 / scale.Z, matrix.M33 / scale.Z, 0, - 0, 0, 0, 1); - - rotation = Quaternion.CreateFromRotationMatrix(rotationMatrix); - } -} \ No newline at end of file diff --git a/Ghost.Engine/Utilities/VectorUtility.cs b/Ghost.Engine/Utilities/VectorUtility.cs deleted file mode 100644 index b875903..0000000 --- a/Ghost.Engine/Utilities/VectorUtility.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Numerics; -using System.Runtime.CompilerServices; - -namespace Ghost.Engine.Utilities; - -public static class VectorUtility -{ - /// - /// Converts a Vector3 representing Euler angles (in degrees) to a Quaternion. - /// - /// The Vector3 containing Euler angles (X: Pitch, Y: Yaw, Z: Roll) in degrees. - /// A Quaternion representing the rotation defined by the Euler angles. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Quaternion ToQuaternion(this Vector3 v) - { - return Quaternion.CreateFromYawPitchRoll(MathUtility.DegToRad(v.Y), MathUtility.DegToRad(v.X), MathUtility.DegToRad(v.Z)); - } - - public static Vector3 CreateFromQuaternion(Quaternion quaternion) - { - // Convert quaternion to Euler angles (Yaw, Pitch, Roll) - quaternion = Quaternion.Normalize(quaternion); - - // Extract pitch (X), yaw (Y), roll (Z) - var ysqr = quaternion.Y * quaternion.Y; - - // Pitch (X-axis rotation) - var t0 = +2.0 * (quaternion.W * quaternion.X + quaternion.Y * quaternion.Z); - var t1 = +1.0 - 2.0 * (quaternion.X * quaternion.X + ysqr); - var pitch = Math.Atan2(t0, t1); - - // Yaw (Y-axis rotation) - var t2 = +2.0 * (quaternion.W * quaternion.Y - quaternion.Z * quaternion.X); - t2 = Math.Clamp(t2, -1.0, 1.0); - var yaw = Math.Asin(t2); - - // Roll (Z-axis rotation) - var t3 = +2.0 * (quaternion.W * quaternion.Z + quaternion.X * quaternion.Y); - var t4 = +1.0 - 2.0 * (ysqr + quaternion.Z * quaternion.Z); - var roll = Math.Atan2(t3, t4); - - const float radToDeg = 180f / MathF.PI; - return new Vector3((float)pitch, (float)yaw, (float)roll) * radToDeg; - } -} \ No newline at end of file diff --git a/Ghost.Entities/Archetype.cs b/Ghost.Entities/Archetype.cs index ff27b17..202934f 100644 --- a/Ghost.Entities/Archetype.cs +++ b/Ghost.Entities/Archetype.cs @@ -70,7 +70,7 @@ internal unsafe sealed class ChunkDebugView return []; } - ref var archetype = ref r.Value.GetArchetypeReference(archetypeID); + ref var archetype = ref r.Value.ComponentManager.GetArchetypeReference(archetypeID); var it = archetype._signature.GetIterator(); while (it.Next(out var index)) { diff --git a/Ghost.Entities/Component.cs b/Ghost.Entities/Component.cs index 2dd0412..a839a82 100644 --- a/Ghost.Entities/Component.cs +++ b/Ghost.Entities/Component.cs @@ -21,65 +21,7 @@ internal struct ComponentInfo public int size; public int alignment; public bool isEnableable; -} - -/// -/// Represents an immutable set of component identifiers used to define a group of components within an entity or system. -/// -public struct ComponentSet : IDisposable, IEquatable -{ - private UnsafeArray> _components; - private int _hashCode; - - public readonly ReadOnlySpan> Components => _components.AsSpan(); - - public ComponentSet(AllocationHandle allocationHandle, params ReadOnlySpan> components) - { - _components = new UnsafeArray>(components.Length, allocationHandle); - components.CopyTo(_components.AsSpan()); - - _hashCode = -1; - } - - public ComponentSet(Allocator allocator, params ReadOnlySpan> components) - : this(AllocationManager.GetAllocationHandle(allocator), components) - { - } - - public readonly bool Equals(ComponentSet other) - { - return _hashCode == other._hashCode; - } - - public override int GetHashCode() - { - if (_hashCode == -1) - { - _hashCode = ComponentRegistry.GetHashCode(_components.AsSpan()); - } - - return _hashCode; - } - - public override bool Equals(object? obj) - { - return obj is ComponentSet set && Equals(set); - } - - public static bool operator ==(ComponentSet left, ComponentSet right) - { - return left.Equals(right); - } - - public static bool operator !=(ComponentSet left, ComponentSet right) - { - return !(left == right); - } - - public void Dispose() - { - _components.Dispose(); - } + public bool isShared; } /// @@ -122,7 +64,7 @@ internal static class ComponentRegistry size = sizeof(T), alignment = (int)MemoryUtility.AlignOf(), isEnableable = typeof(IEnableableComponent).IsAssignableFrom(type), - // isManaged = typeof(IManagedWrapper).IsAssignableFrom(space), + // isShared = typeof(ISharedComponent).IsAssignableFrom(type), }; s_registeredComponents.Add(info); @@ -198,3 +140,191 @@ internal static class ComponentRegistry return bitSet.GetHashCode(); } } + +public class ComponentManager : IDisposable +{ + private readonly World _world; + + private UnsafeList _archetypes; + private UnsafeList _entityQueries; + + private UnsafeHashMap> _archetypeLookup; // Signature Hash to Archetype ID + private UnsafeHashMap> _querieLookup; // Query Mask Hash to Query ID + + private bool _isDisposed; + + public int ArchetypeCount => _archetypes.Count; + + internal ComponentManager(World world) + { + _world = world; + + _archetypes = new UnsafeList(16, Allocator.Persistent); + _entityQueries = new UnsafeList(16, Allocator.Persistent); + + _archetypeLookup = new UnsafeHashMap>(16, Allocator.Persistent); + _querieLookup = new UnsafeHashMap>(16, Allocator.Persistent); + + // Create the empty archetype + CreateArchetype(ReadOnlySpan>.Empty, 0); + } + + ~ComponentManager() + { + Dispose(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Identifier CreateArchetype(ReadOnlySpan> componentTypeIDs, int signatureHash) + { + var arcID = new Identifier(_archetypes.Count); + _archetypes.Add(new Archetype(arcID, _world.ID, componentTypeIDs)); + _archetypeLookup.Add(signatureHash, arcID); + + for (int i = 0; i < _entityQueries.Count; i++) + { + ref var query = ref _entityQueries[i]; + query.AddArchetypeIfMatch(in _archetypes[arcID.Value]); + } + + return arcID; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Identifier GetArchetypeIDBySignatureHash(int signatureHash) + { + if (_archetypeLookup.TryGetValue(signatureHash, out var arcID)) + { + return arcID; + } + + return Identifier.Invalid; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref Archetype GetArchetypeReference(Identifier id) + { + return ref _archetypes[id.Value]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Identifier CreateEntityQuery(EntityQueryMask mask, int maskHash) + { + var queryID = new Identifier(_entityQueries.Count); + _entityQueries.Add(new EntityQuery(queryID, _world.ID, mask)); + _querieLookup.Add(maskHash, queryID); + + ref var query = ref _entityQueries[queryID.Value]; + for (var i = 0; i < _archetypes.Count; i++) + { + query.AddArchetypeIfMatch(in _archetypes[i]); + } + + return queryID; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Identifier GetEntityQueryIDByMaskHash(int maskHash) + { + if (_querieLookup.TryGetValue(maskHash, out var queryID)) + { + return queryID; + } + + return Identifier.Invalid; + } + + /// + /// Gets a reference to the entity query with the specified identifier. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref EntityQuery GetEntityQueryReference(Identifier id) + { + return ref _entityQueries[id.Value]; + } + + public void Dispose() + { + if (_isDisposed) + { + return; + } + + foreach (ref var archetype in _archetypes) + { + archetype.Dispose(); + } + + foreach (ref var query in _entityQueries) + { + query.Dispose(); + } + + _archetypes.Dispose(); + _entityQueries.Dispose(); + _archetypeLookup.Dispose(); + _querieLookup.Dispose(); + + _isDisposed = true; + GC.SuppressFinalize(this); + } +} + +/// +/// Represents an immutable set of component identifiers used to define a group of components within an entity or system. +/// +public struct ComponentSet : IDisposable, IEquatable +{ + private UnsafeArray> _components; + private int _hashCode; + + public readonly ReadOnlySpan> Components => _components.AsSpan(); + + public ComponentSet(AllocationHandle allocationHandle, params ReadOnlySpan> components) + { + _components = new UnsafeArray>(components.Length, allocationHandle); + components.CopyTo(_components.AsSpan()); + + _hashCode = -1; + } + + public ComponentSet(Allocator allocator, params ReadOnlySpan> components) + : this(AllocationManager.GetAllocationHandle(allocator), components) + { + } + + public readonly bool Equals(ComponentSet other) + { + return _hashCode == other._hashCode; + } + + public override int GetHashCode() + { + if (_hashCode == -1) + { + _hashCode = ComponentRegistry.GetHashCode(_components.AsSpan()); + } + + return _hashCode; + } + + public override readonly bool Equals(object? obj) + { + return obj is ComponentSet set && Equals(set); + } + + public static bool operator ==(ComponentSet left, ComponentSet right) + { + return left.Equals(right); + } + + public static bool operator !=(ComponentSet left, ComponentSet right) + { + return !(left == right); + } + + public void Dispose() + { + _components.Dispose(); + } +} diff --git a/Ghost.Entities/EntityManager.Managed.cs b/Ghost.Entities/EntityManager.Managed.cs index b4dfef9..ee1f06d 100644 --- a/Ghost.Entities/EntityManager.Managed.cs +++ b/Ghost.Entities/EntityManager.Managed.cs @@ -108,7 +108,7 @@ public partial class EntityManager where T : ScriptComponent, new() { var location = _entityLocations.GetElementAt(entity.ID, entity.Generation); - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); var pManagedEntityRef = (ManagedEntityRef*)archetype.GetComponentData(location.chunkIndex, location.rowIndex, ComponentTypeID.Value); if (pManagedEntityRef == null) diff --git a/Ghost.Entities/EntityManager.cs b/Ghost.Entities/EntityManager.cs index 5cc9e28..872787a 100644 --- a/Ghost.Entities/EntityManager.cs +++ b/Ghost.Entities/EntityManager.cs @@ -116,7 +116,7 @@ public unsafe partial class EntityManager : IDisposable /// The span to store the created entities. public void CreateEntities(Span entities) { - ref var emptyArchetype = ref _world.GetArchetypeReference(World.EmptyArchetypeID); + ref var emptyArchetype = ref _world.ComponentManager.GetArchetypeReference(World.EmptyArchetypeID); emptyArchetype.AllocateEntity(out var chunkIndex, out var rowIndex); for (var i = 0; i < entities.Length; i++) @@ -141,7 +141,7 @@ public unsafe partial class EntityManager : IDisposable /// The number of entities to create. public void CreateEntities(int count) { - ref var emptyArchetype = ref _world.GetArchetypeReference(World.EmptyArchetypeID); + ref var emptyArchetype = ref _world.ComponentManager.GetArchetypeReference(World.EmptyArchetypeID); emptyArchetype.AllocateEntity(out var chunkIndex, out var rowIndex); for (var i = 0; i < count; i++) @@ -167,14 +167,14 @@ public unsafe partial class EntityManager : IDisposable public void CreateEntities(Span entities, ComponentSet set) { var hash = set.GetHashCode(); - var arcID = _world.GetArchetypeIDBySignatureHash(hash); + var arcID = _world.ComponentManager.GetArchetypeIDBySignatureHash(hash); if (arcID.IsInvalid) { - arcID = _world.CreateArchetype(set.Components, hash); + arcID = _world.ComponentManager.CreateArchetype(set.Components, hash); } - ref var archetype = ref _world.GetArchetypeReference(arcID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(arcID); for (var i = 0; i < entities.Length; i++) { @@ -202,14 +202,14 @@ public unsafe partial class EntityManager : IDisposable public void CreateEntities(int count, ComponentSet set) { var hash = set.GetHashCode(); - var arcID = _world.GetArchetypeIDBySignatureHash(hash); + var arcID = _world.ComponentManager.GetArchetypeIDBySignatureHash(hash); if (arcID.IsInvalid) { - arcID = _world.CreateArchetype(set.Components, hash); + arcID = _world.ComponentManager.CreateArchetype(set.Components, hash); } - ref var archetype = ref _world.GetArchetypeReference(arcID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(arcID); for (var i = 0; i < count; i++) { @@ -247,7 +247,7 @@ public unsafe partial class EntityManager : IDisposable return ErrorStatus.NotFound; } - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); DestoryManagedEntityIfExists(in archetype, location); var r = archetype.RemoveEntity(location.chunkIndex, location.rowIndex); @@ -332,7 +332,7 @@ public unsafe partial class EntityManager : IDisposable { // FLUSH PREVIOUS BATCH // We must retrieve the Archetype of the *Previous* batch, not the current 'loc' - ref var prevArchetype = ref _world.GetArchetypeReference(prevArchetypeID); + ref var prevArchetype = ref _world.ComponentManager.GetArchetypeReference(prevArchetypeID); // Remove Managed Entities first RemoveManagedEntity(rowIndicesCache.AsSpan(), in prevArchetype, prevChunkIndex); @@ -353,7 +353,7 @@ public unsafe partial class EntityManager : IDisposable // Process the stragglers remaining in the cache if (rowIndicesCache.Count > 0) { - ref var lastArchetype = ref _world.GetArchetypeReference(prevArchetypeID); + ref var lastArchetype = ref _world.ComponentManager.GetArchetypeReference(prevArchetypeID); RemoveManagedEntity(rowIndicesCache.AsSpan(), in lastArchetype, prevChunkIndex); lastArchetype.RemoveEntities(prevChunkIndex, rowIndicesCache.AsSpan()); @@ -392,16 +392,16 @@ public unsafe partial class EntityManager : IDisposable // Check if singleton already exists var signatureHash = ComponentRegistry.GetHashCode(componentID); - var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); + var arcID = _world.ComponentManager.GetArchetypeIDBySignatureHash(signatureHash); if (arcID.IsValid) { return ErrorStatus.InvalidArgument; } - arcID = _world.CreateArchetype([componentID], signatureHash); + arcID = _world.ComponentManager.CreateArchetype([componentID], signatureHash); - ref var archetype = ref _world.GetArchetypeReference(arcID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(arcID); archetype.AllocateEntity(out var chunkIndex, out var rowIndex); var id = _entityLocations.Add(new EntityLocation @@ -438,14 +438,14 @@ public unsafe partial class EntityManager : IDisposable public void* GetSingleton(Identifier componentID) { var signatureHash = ComponentRegistry.GetHashCode(componentID); - var arcID = _world.GetArchetypeIDBySignatureHash(signatureHash); + var arcID = _world.ComponentManager.GetArchetypeIDBySignatureHash(signatureHash); if (arcID.IsInvalid) { return null; } - ref var archetype = ref _world.GetArchetypeReference(arcID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(arcID); var layoutResult = archetype.GetLayout(componentID); if (layoutResult.Error != ErrorStatus.None) { @@ -510,7 +510,7 @@ public unsafe partial class EntityManager : IDisposable } // Build new archetype signature - ref var oldArchetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var oldArchetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); var oldSignature = oldArchetype._signature; if (oldSignature.IsSet(componentID)) @@ -543,7 +543,7 @@ public unsafe partial class EntityManager : IDisposable // Find or create new archetype var newSignatureHash = newSignature.GetHashCode(); - newArcID = _world.GetArchetypeIDBySignatureHash(newSignatureHash); + newArcID = _world.ComponentManager.GetArchetypeIDBySignatureHash(newSignatureHash); if (newArcID.IsInvalid) { // Create new archetype @@ -556,14 +556,14 @@ public unsafe partial class EntityManager : IDisposable componentTypeIDs[i++] = index; } - newArcID = _world.CreateArchetype(componentTypeIDs, newSignatureHash); + newArcID = _world.ComponentManager.CreateArchetype(componentTypeIDs, newSignatureHash); } oldArchetype.AddEdgeAdd(componentID, newArcID); } // Move entity data - ref var newArchetype = ref _world.GetArchetypeReference(newArcID); + ref var newArchetype = ref _world.ComponentManager.GetArchetypeReference(newArcID); newArchetype.AllocateEntity(out var newChunkIndex, out var newRowIndex); CopyData(ref oldArchetype, location.chunkIndex, location.rowIndex, ref newArchetype, newChunkIndex, newRowIndex); @@ -615,7 +615,7 @@ public unsafe partial class EntityManager : IDisposable } // Build new archetype signature - ref var oldArchetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var oldArchetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); var oldSignature = oldArchetype._signature; var newArcID = oldArchetype.GetEdgeRemove(componentID); @@ -642,7 +642,7 @@ public unsafe partial class EntityManager : IDisposable // Find or create new archetype var newSignatureHash = newSignature.GetHashCode(); - newArcID = _world.GetArchetypeIDBySignatureHash(newSignatureHash); + newArcID = _world.ComponentManager.GetArchetypeIDBySignatureHash(newSignatureHash); if (newArcID.IsInvalid) { // Create new archetype @@ -655,14 +655,14 @@ public unsafe partial class EntityManager : IDisposable componentTypeIDs[i++] = index; } - newArcID = _world.CreateArchetype(componentTypeIDs, newSignatureHash); + newArcID = _world.ComponentManager.CreateArchetype(componentTypeIDs, newSignatureHash); } oldArchetype.AddEdgeRemove(componentID, newArcID); } // Move entity data - ref var newArchetype = ref _world.GetArchetypeReference(newArcID); + ref var newArchetype = ref _world.ComponentManager.GetArchetypeReference(newArcID); newArchetype.AllocateEntity(out var newChunkIndex, out var newRowIndex); CopyData(ref oldArchetype, location.chunkIndex, location.rowIndex, ref newArchetype, newChunkIndex, newRowIndex); @@ -716,7 +716,7 @@ public unsafe partial class EntityManager : IDisposable return ErrorStatus.NotFound; } - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); archetype.SetComponentData(location.chunkIndex, location.rowIndex, componentID, pComponent); return ErrorStatus.None; @@ -747,7 +747,7 @@ public unsafe partial class EntityManager : IDisposable return null; } - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); return archetype.GetComponentData(location.chunkIndex, location.rowIndex, componentID); } @@ -777,7 +777,7 @@ public unsafe partial class EntityManager : IDisposable return false; } - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); return archetype.HasComponent(componentID); } @@ -807,7 +807,7 @@ public unsafe partial class EntityManager : IDisposable return ErrorStatus.NotFound; } - ref var archetype = ref _world.GetArchetypeReference(location.archetypeID); + ref var archetype = ref _world.ComponentManager.GetArchetypeReference(location.archetypeID); var chunkIndex = location.chunkIndex; var rowIndex = location.rowIndex; diff --git a/Ghost.Entities/EntityQuery.JobChunk.cs b/Ghost.Entities/EntityQuery.JobChunk.cs index 0248a30..fd04521 100644 --- a/Ghost.Entities/EntityQuery.JobChunk.cs +++ b/Ghost.Entities/EntityQuery.JobChunk.cs @@ -57,7 +57,7 @@ public unsafe partial struct EntityQuery foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); for (int i = 0; i < arch.ChunkCount; i++) { diff --git a/Ghost.Entities/Query.cs b/Ghost.Entities/Query.cs index 9cab976..003ea0e 100644 --- a/Ghost.Entities/Query.cs +++ b/Ghost.Entities/Query.cs @@ -285,7 +285,7 @@ public unsafe partial struct EntityQuery : IDisposable { get { - ref var archetype = ref _iterator._world.GetArchetypeReference(_iterator._matchingArchetypes[_archetypeIndex]); + ref var archetype = ref _iterator._world.ComponentManager.GetArchetypeReference(_iterator._matchingArchetypes[_archetypeIndex]); ref var chunk = ref archetype.GetChunkReference(_chunkIndex); return new ChunkView(in archetype, in chunk); } @@ -297,7 +297,7 @@ public unsafe partial struct EntityQuery : IDisposable while (_archetypeIndex < _iterator._matchingArchetypes.Count) { - ref var archetype = ref _iterator._world.GetArchetypeReference(_iterator._matchingArchetypes[_archetypeIndex]); + ref var archetype = ref _iterator._world.ComponentManager.GetArchetypeReference(_iterator._matchingArchetypes[_archetypeIndex]); if (_chunkIndex < archetype.ChunkCount) { return true; @@ -452,7 +452,7 @@ public unsafe partial struct EntityQuery : IDisposable for(var i = 0; i < _matchingArchetypes.Count; i++) { var archetypeID = _matchingArchetypes[i]; - ref var archetype = ref world.GetArchetypeReference(archetypeID); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(archetypeID); for (var j = 0; j < archetype.ChunkCount; j++) { ref var chunk = ref archetype.GetChunkReference(j); @@ -577,12 +577,12 @@ public ref partial struct QueryBuilder // 4. Ask World for the Query (Cached) var maskHash = mask.GetHashCode(); - var queryID = world.GetEntityQueryIDByMaskHash(maskHash); + var queryID = world.ComponentManager.GetEntityQueryIDByMaskHash(maskHash); if (queryID.IsValid) { // Check if the masks are actually equal (Hash collision?). // Really worth it? It's unlikely to have collisions here. - if (world.GetEntityQueryReference(queryID)._mask.Equals(mask)) + if (world.ComponentManager.GetEntityQueryReference(queryID)._mask.Equals(mask)) { mask.Dispose(); goto Return; @@ -590,7 +590,7 @@ public ref partial struct QueryBuilder } // NOTE: We do not dispose the mask here, as it is now owned by the EntityQuery. - queryID = world.CreateEntityQuery(mask, maskHash); + queryID = world.ComponentManager.CreateEntityQuery(mask, maskHash); Return: Dispose(); diff --git a/Ghost.Entities/SharedComponent.cs b/Ghost.Entities/SharedComponent.cs new file mode 100644 index 0000000..1e2e259 --- /dev/null +++ b/Ghost.Entities/SharedComponent.cs @@ -0,0 +1,176 @@ +#if false // FIX: API update in Misaki.HighPerformance.LowLevel.Collections require me to disable this for now. +using Misaki.HighPerformance.LowLevel.Buffer; +using Misaki.HighPerformance.LowLevel.Collections; +using Misaki.HighPerformance.LowLevel.Utilities; + +namespace Ghost.Entities; + +public interface ISharedComponent +{ +} + +internal unsafe sealed class SharedComponentStore : IDisposable +{ + private struct EntryInfo + { + public int RefCount; + public int HashCode; + public int Version; + public int NextFree; // free-list linkage (index) + } + + private struct TypeStore : IDisposable + { + public int TypeSize; + public UnsafeList Data; // raw bytes, stride = TypeSize + public UnsafeList Infos; // parallel to Data entries (Entry 0 reserved) + public UnsafeHashMap HashLookup; // (hashKey) -> entryIndex + public int FreeListHead; // head index, 0 means none (we'll use Infos[0].NextFree too if you prefer) + public int VersionCounter; + + public void Dispose() + { + Data.Dispose(); + Infos.Dispose(); + HashLookup.Dispose(); + } + } + + private readonly UnsafeHashMap _perType; // componentTypeId -> TypeStore + + public SharedComponentStore(int initialCapacity = 16) + { + _perType = new UnsafeHashMap(initialCapacity, Allocator.Persistent); + } + + public void Dispose() + { + foreach (var kvp in _perType) + { + kvp.Value.Dispose(); + } + + _perType.Dispose(); + } + + public int InsertOrGet(int componentTypeId, int typeSize, void* data, int hashCode) + { + // Reserve index 0 for "default" + if (data == null) + { + return 0; + } + + ref var store = ref GetOrCreateTypeStore(componentTypeId, typeSize); + + // Combine (typeId, hash) into a single key; collisions handled by memcmp below. + var key = ((long)componentTypeId << 32) ^ (uint)hashCode; + + if (store.HashLookup.TryGetValue(key, out var existingIndex)) + { + var existingPtr = (byte*)store.Data.GetUnsafePtr() + (existingIndex * store.TypeSize); + if (new Span(existingPtr, store.TypeSize).SequenceEqual(new Span(data, store.TypeSize))) + { + ((EntryInfo*)store.Infos.GetUnsafePtr())[existingIndex].RefCount++; + return existingIndex; + } + // If collision: fall through to insert (you may want a secondary structure). + } + + int index = AllocateEntry(ref store); + + var dst = (byte*)store.Data.GetUnsafePtr() + (index * store.TypeSize); + MemoryUtility.MemCpy(dst, data, (nuint)store.TypeSize); + + store.Infos[index] = new EntryInfo + { + RefCount = 1, + HashCode = hashCode, + Version = ++store.VersionCounter, + NextFree = -1 + }; + + store.HashLookup[key] = index; + return index; + } + + public void AddRef(int componentTypeId, int index) + { + if (index == 0) return; + ref var store = ref _perType[componentTypeId]; + store.Infos[index].RefCount++; + } + + public void Release(int componentTypeId, int index) + { + if (index == 0) return; + ref var store = ref _perType.GetValueByKey(componentTypeId); + + ref var info = ref store.Infos.Ptr[index]; + info.RefCount--; + if (info.RefCount > 0) return; + + // Remove from hash lookup (best-effort; collisions require more robust handling) + long key = ((long)componentTypeId << 32) ^ (uint)info.HashCode; + store.HashLookup.Remove(key); + + // Push to free-list + info.NextFree = store.FreeListHead; + store.FreeListHead = index; + } + + public void* GetDataPtr(int componentTypeId, int index) + { + if (index == 0) return null; + ref var store = ref _perType.GetValueByKey(componentTypeId); + return (byte*)store.Data.Ptr + (index * store.TypeSize); + } + + private ref TypeStore GetOrCreateTypeStore(int componentTypeId, int typeSize) + { + if (_perType.TryGetValue(componentTypeId, out var existing)) + { + // UnsafeHashMap returns by value in some implementations; you may need a different pattern here. + // Adjust to your container API (e.g., TryGetValueRef). + } + + var store = new TypeStore + { + TypeSize = typeSize, + Data = new UnsafeList(typeSize * 16, Allocator.Persistent), + Infos = new UnsafeList(16, Allocator.Persistent), + HashLookup = new UnsafeHashMap(16, Allocator.Persistent), + FreeListHead = 0, + VersionCounter = 0 + }; + + // Create reserved default entry at index 0 + store.Data.Resize(typeSize); // one element worth of bytes + store.Infos.Add(new EntryInfo { RefCount = int.MaxValue, HashCode = 0, Version = 0, NextFree = -1 }); + + _perType.Add(componentTypeId, store); + // NOTE: returning a ref requires a "get ref" API; adjust to your UnsafeHashMap capabilities. + return ref _perType.GetValueByKey(componentTypeId); + } + + private static int AllocateEntry(ref TypeStore store) + { + if (store.FreeListHead != 0) + { + int idx = store.FreeListHead; + store.FreeListHead = store.Infos[idx].NextFree; + store.Infos[idx].NextFree = -1; + return idx; + } + + int newIndex = store.Infos.Count; + store.Infos.Add(default); + + int newByteCount = (newIndex + 1) * store.TypeSize; + store.Data.Resize(newByteCount); + + return newIndex; + } +} + +#endif diff --git a/Ghost.Entities/System.cs b/Ghost.Entities/System.cs index de8be0d..fcd7477 100644 --- a/Ghost.Entities/System.cs +++ b/Ghost.Entities/System.cs @@ -45,7 +45,7 @@ public abstract class SystemBase : ISystem foreach (var queryID in _requiredQueries) { - ref var query = ref World.GetEntityQueryReference(new Identifier(queryID)); + ref var query = ref World.ComponentManager.GetEntityQueryReference(new Identifier(queryID)); if (query.GetEntityCount() == 0) { return false; @@ -197,7 +197,7 @@ public abstract class SystemGroup : ISystem private static List Sort(List systems) { // 1. Build the Graph - // Key: The System, Value: Systems that MUST run before the Key + // Key64: The System, Value: Systems that MUST run before the Key64 var dependencies = new Dictionary>(); var systemMap = systems.ToDictionary(s => s.GetType(), s => s); diff --git a/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs index f28cca8..ead2e68 100644 --- a/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs +++ b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.gen.cs @@ -113,7 +113,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -142,7 +142,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -309,7 +309,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -338,7 +338,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -515,7 +515,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -544,7 +544,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -731,7 +731,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -760,7 +760,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -957,7 +957,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -986,7 +986,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1193,7 +1193,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1222,7 +1222,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1439,7 +1439,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1468,7 +1468,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1695,7 +1695,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1724,7 +1724,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); diff --git a/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt index f57171f..4c932d2 100644 --- a/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt +++ b/Ghost.Entities/Templates/EntityQuery.ComponentIterator.tt @@ -158,7 +158,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -187,7 +187,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); diff --git a/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.gen.cs b/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.gen.cs index 3b6c744..d0b0638 100644 --- a/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.gen.cs +++ b/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.gen.cs @@ -136,7 +136,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -165,7 +165,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -339,7 +339,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -368,7 +368,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -552,7 +552,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -581,7 +581,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -775,7 +775,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -804,7 +804,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1008,7 +1008,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1037,7 +1037,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1251,7 +1251,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1280,7 +1280,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1504,7 +1504,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1533,7 +1533,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); @@ -1767,7 +1767,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -1796,7 +1796,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); diff --git a/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.tt b/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.tt index 9b61b3b..9820a67 100644 --- a/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.tt +++ b/Ghost.Entities/Templates/EntityQuery.EntityComponentIterator.tt @@ -159,7 +159,7 @@ public unsafe partial struct EntityQuery _currentArchetypeIndex++; if (_currentArchetypeIndex < _matchingArchetypes.Count) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[_currentArchetypeIndex]); _currentChunkIndex = 0; if (_currentArchetype.ChunkCount > 0) @@ -188,7 +188,7 @@ public unsafe partial struct EntityQuery if (_matchingArchetypes.Count > 0) { - _currentArchetype = ref _world.GetArchetypeReference(_matchingArchetypes[0]); + _currentArchetype = ref _world.ComponentManager.GetArchetypeReference(_matchingArchetypes[0]); if (_currentArchetype.ChunkCount > 0) { SetChunk(0); diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs index 0f9c633..7f185c6 100644 --- a/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs +++ b/Ghost.Entities/Templates/EntityQuery.ForEach.gen.cs @@ -37,7 +37,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 1; index++) { @@ -124,7 +124,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 2; index++) { @@ -215,7 +215,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 3; index++) { @@ -310,7 +310,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 4; index++) { @@ -409,7 +409,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 5; index++) { @@ -512,7 +512,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 6; index++) { @@ -619,7 +619,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 7; index++) { @@ -730,7 +730,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 8; index++) { @@ -821,7 +821,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 1; index++) { @@ -909,7 +909,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 2; index++) { @@ -1001,7 +1001,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 3; index++) { @@ -1097,7 +1097,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 4; index++) { @@ -1197,7 +1197,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 5; index++) { @@ -1301,7 +1301,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 6; index++) { @@ -1409,7 +1409,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 7; index++) { @@ -1521,7 +1521,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < 8; index++) { diff --git a/Ghost.Entities/Templates/EntityQuery.ForEach.tt b/Ghost.Entities/Templates/EntityQuery.ForEach.tt index 23edaae..e45d200 100644 --- a/Ghost.Entities/Templates/EntityQuery.ForEach.tt +++ b/Ghost.Entities/Templates/EntityQuery.ForEach.tt @@ -58,7 +58,7 @@ public unsafe partial struct EntityQuery for (var i = 0; i < _matchingArchetypes.Count; i++) { - ref var archetype = ref world.GetArchetypeReference(_matchingArchetypes[i]); + ref var archetype = ref world.ComponentManager.GetArchetypeReference(_matchingArchetypes[i]); var hasAllComponents = true; for (var index = 0; index < <#= i #>; index++) { diff --git a/Ghost.Entities/Templates/EntityQuery.JobEntity.gen.cs b/Ghost.Entities/Templates/EntityQuery.JobEntity.gen.cs index 62b1dcd..06728cd 100644 --- a/Ghost.Entities/Templates/EntityQuery.JobEntity.gen.cs +++ b/Ghost.Entities/Templates/EntityQuery.JobEntity.gen.cs @@ -1114,7 +1114,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1255,7 +1255,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1423,7 +1423,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1618,7 +1618,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -1840,7 +1840,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -2089,7 +2089,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -2365,7 +2365,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { @@ -2668,7 +2668,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { diff --git a/Ghost.Entities/Templates/EntityQuery.JobEntity.tt b/Ghost.Entities/Templates/EntityQuery.JobEntity.tt index fd020e7..b170cf1 100644 --- a/Ghost.Entities/Templates/EntityQuery.JobEntity.tt +++ b/Ghost.Entities/Templates/EntityQuery.JobEntity.tt @@ -146,7 +146,7 @@ public unsafe partial struct EntityQuery // Iterate the Query's matching archetypes foreach (var archID in _matchingArchetypes) { - ref var arch = ref world.GetArchetypeReference(archID); + ref var arch = ref world.ComponentManager.GetArchetypeReference(archID); if (arch.ChunkCount == 0) { diff --git a/Ghost.Entities/Templates/ForEach.gen.cs b/Ghost.Entities/Templates/ForEach.gen.cs index bbb85de..27f45d9 100644 --- a/Ghost.Entities/Templates/ForEach.gen.cs +++ b/Ghost.Entities/Templates/ForEach.gen.cs @@ -1,3 +1,4 @@ + namespace Ghost.Entities; public delegate void ForEach(ref T0 component0) diff --git a/Ghost.Entities/Templates/QueryBuilder.With.gen.cs b/Ghost.Entities/Templates/QueryBuilder.With.gen.cs index 83f1cea..eb3787f 100644 --- a/Ghost.Entities/Templates/QueryBuilder.With.gen.cs +++ b/Ghost.Entities/Templates/QueryBuilder.With.gen.cs @@ -6,7 +6,7 @@ namespace Ghost.Entities; public ref partial struct QueryBuilder { /// - /// Adds the specified component space(s) to the 'All' filter of the query. + /// Adds the specified component type(s) to the 'All' filter of the query. /// Targets entities that have all of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -19,7 +19,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'All' filter of the query and requires read-write access. + /// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access. /// Targets entities that have all of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -33,7 +33,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Any' filter of the query. + /// Adds the specified component type(s) to the 'Any' filter of the query. /// Targets entities that have at least one of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -46,7 +46,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Absent' filter of the query. + /// Adds the specified component type(s) to the 'Absent' filter of the query. /// Targets entities that do not have any of the specified component types. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -59,7 +59,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'None' filter of the query. + /// Adds the specified component type(s) to the 'None' filter of the query. /// Targets entities that do not have any of the specified component types, or those component(s) are disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -72,7 +72,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Disabled' filter of the query. + /// Adds the specified component type(s) to the 'Disabled' filter of the query. /// Targets entities that have all of the specified component types and those component(s) are disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -85,7 +85,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Present' filter of the query. + /// Adds the specified component type(s) to the 'Present' filter of the query. /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -98,7 +98,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Present' filter of the query and requires read-write access. + /// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access. /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -112,7 +112,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'All' filter of the query. + /// Adds the specified component type(s) to the 'All' filter of the query. /// Targets entities that have all of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -127,7 +127,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'All' filter of the query and requires read-write access. + /// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access. /// Targets entities that have all of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -144,7 +144,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Any' filter of the query. + /// Adds the specified component type(s) to the 'Any' filter of the query. /// Targets entities that have at least one of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -159,7 +159,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Absent' filter of the query. + /// Adds the specified component type(s) to the 'Absent' filter of the query. /// Targets entities that do not have any of the specified component types. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -174,7 +174,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'None' filter of the query. + /// Adds the specified component type(s) to the 'None' filter of the query. /// Targets entities that do not have any of the specified component types, or those component(s) are disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -189,7 +189,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Disabled' filter of the query. + /// Adds the specified component type(s) to the 'Disabled' filter of the query. /// Targets entities that have all of the specified component types and those component(s) are disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -204,7 +204,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Present' filter of the query. + /// Adds the specified component type(s) to the 'Present' filter of the query. /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -219,7 +219,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Present' filter of the query and requires read-write access. + /// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access. /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -236,7 +236,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'All' filter of the query. + /// Adds the specified component type(s) to the 'All' filter of the query. /// Targets entities that have all of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -253,7 +253,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'All' filter of the query and requires read-write access. + /// Adds the specified component type(s) to the 'All' filter of the query and requires read-write access. /// Targets entities that have all of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -273,7 +273,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Any' filter of the query. + /// Adds the specified component type(s) to the 'Any' filter of the query. /// Targets entities that have at least one of the specified component types and those component(s) must be enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -290,7 +290,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Absent' filter of the query. + /// Adds the specified component type(s) to the 'Absent' filter of the query. /// Targets entities that do not have any of the specified component types. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -307,7 +307,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'None' filter of the query. + /// Adds the specified component type(s) to the 'None' filter of the query. /// Targets entities that do not have any of the specified component types, or those component(s) are disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -324,7 +324,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Disabled' filter of the query. + /// Adds the specified component type(s) to the 'Disabled' filter of the query. /// Targets entities that have all of the specified component types and those component(s) are disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -341,7 +341,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Present' filter of the query. + /// Adds the specified component type(s) to the 'Present' filter of the query. /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -358,7 +358,7 @@ public ref partial struct QueryBuilder } /// - /// Adds the specified component space(s) to the 'Present' filter of the query and requires read-write access. + /// Adds the specified component type(s) to the 'Present' filter of the query and requires read-write access. /// Targets entities that have all of the specified component types, regardless of whether those component(s) are enabled or disabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Ghost.Entities/World.cs b/Ghost.Entities/World.cs index 0aab614..100c5fc 100644 --- a/Ghost.Entities/World.cs +++ b/Ghost.Entities/World.cs @@ -1,7 +1,5 @@ using Ghost.Core; using Misaki.HighPerformance.Jobs; -using Misaki.HighPerformance.LowLevel.Buffer; -using Misaki.HighPerformance.LowLevel.Collections; using System.Runtime.CompilerServices; namespace Ghost.Entities; @@ -85,19 +83,12 @@ public partial class World : IDisposable, IEquatable private readonly EntityCommandBuffer _entityCommandBuffer; private readonly EntityCommandBuffer[]? _threadLocalECBs; + private readonly ComponentManager _componentManager; private readonly SystemManager _systemManager; - private UnsafeList _archetypes; - private UnsafeList _entityQueries; - - private UnsafeHashMap> _archetypeLookup; // Signature Hash to Archetype ID - private UnsafeHashMap> _querieLookup; // Query Mask Hash to Query ID - private int _version; private bool _disposed = false; - internal int ArchetypeCount => _archetypes.Count; - /// /// Gets the unique identifier of this world. /// @@ -113,6 +104,11 @@ public partial class World : IDisposable, IEquatable /// public EntityManager EntityManager => _entityManager; + /// + /// Gets the component manager for this world. + /// + public ComponentManager ComponentManager => _componentManager; + /// /// Gets the system manager for this world. /// @@ -139,14 +135,9 @@ public partial class World : IDisposable, IEquatable _entityManager = new EntityManager(this, entityCapacity); _entityCommandBuffer = new EntityCommandBuffer(_entityManager); + _componentManager = new ComponentManager(this); _systemManager = new SystemManager(this); - _archetypes = new UnsafeList(16, Allocator.Persistent); - _entityQueries = new UnsafeList(16, Allocator.Persistent); - - _archetypeLookup = new UnsafeHashMap>(16, Allocator.Persistent); - _querieLookup = new UnsafeHashMap>(16, Allocator.Persistent); - if (jobScheduler != null) { _threadLocalECBs = new EntityCommandBuffer[jobScheduler.WorkerCount]; @@ -155,9 +146,6 @@ public partial class World : IDisposable, IEquatable _threadLocalECBs[i] = new EntityCommandBuffer(_entityManager); } } - - // Create the empty archetype - CreateArchetype(ReadOnlySpan>.Empty, 0); } ~World() @@ -165,66 +153,6 @@ public partial class World : IDisposable, IEquatable Dispose(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Identifier CreateArchetype(ReadOnlySpan> componentTypeIDs, int signatureHash) - { - var arcID = new Identifier(_archetypes.Count); - _archetypes.Add(new Archetype(arcID, _id, componentTypeIDs)); - _archetypeLookup.Add(signatureHash, arcID); - - for (int i = 0; i < _entityQueries.Count; i++) - { - ref var query = ref _entityQueries[i]; - query.AddArchetypeIfMatch(in _archetypes[arcID.Value]); - } - - return arcID; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Identifier GetArchetypeIDBySignatureHash(int signatureHash) - { - if (_archetypeLookup.TryGetValue(signatureHash, out var arcID)) - { - return arcID; - } - - return Identifier.Invalid; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ref Archetype GetArchetypeReference(Identifier id) - { - return ref _archetypes[id.Value]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Identifier CreateEntityQuery(EntityQueryMask mask, int maskHash) - { - var queryID = new Identifier(_entityQueries.Count); - _entityQueries.Add(new EntityQuery(queryID, _id, mask)); - _querieLookup.Add(maskHash, queryID); - - ref var query = ref _entityQueries[queryID.Value]; - for (var i = 0; i < _archetypes.Count; i++) - { - query.AddArchetypeIfMatch(in _archetypes[i]); - } - - return queryID; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Identifier GetEntityQueryIDByMaskHash(int maskHash) - { - if (_querieLookup.TryGetValue(maskHash, out var queryID)) - { - return queryID; - } - - return Identifier.Invalid; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void PlaybackEntityCommandBuffers() { @@ -245,15 +173,6 @@ public partial class World : IDisposable, IEquatable return Interlocked.Increment(ref _version); } - /// - /// Gets a reference to the entity query with the specified identifier. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref EntityQuery GetEntityQueryReference(Identifier id) - { - return ref _entityQueries[id.Value]; - } - /// /// Gets the thread-local entity command buffer for the specified thread index. /// @@ -300,16 +219,6 @@ public partial class World : IDisposable, IEquatable return; } - foreach (ref var archetype in _archetypes) - { - archetype.Dispose(); - } - - foreach (ref var query in _entityQueries) - { - query.Dispose(); - } - _entityManager.Dispose(); _entityCommandBuffer.Dispose(); @@ -321,11 +230,6 @@ public partial class World : IDisposable, IEquatable } } - _archetypes.Dispose(); - _entityQueries.Dispose(); - _archetypeLookup.Dispose(); - _querieLookup.Dispose(); - s_freeWorldSlots.Enqueue(_id); s_worlds[_id] = null; diff --git a/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj b/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj index 356b416..418ca63 100644 --- a/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj +++ b/Ghost.Graphics.Test/Ghost.Graphics.Test.csproj @@ -15,6 +15,7 @@ + @@ -56,6 +57,11 @@ + + + MSBuild:Compile + + +- [Architecture Design Document](#architecture-design-document) + - [Ghost Shader Concept - Technical Deep Dive](#ghost-shader-concept-technical-deep-dive) + - [Overview](#overview) + - [Memory Layout & Cache Efficiency](#memory-layout-cache-efficiency) + - [KeywordSet (64 bytes, cache-line friendly)](#keywordset-64-bytes-cache-line-friendly) + - [MaterialPropertyBlock (Variable Size, GPU-aligned)](#materialpropertyblock-variable-size-gpu-aligned) + - [Variant Compilation & Caching](#variant-compilation-caching) + - [Two-Level Caching Strategy](#two-level-caching-strategy) + - [Batching Algorithm](#batching-algorithm) + - [Phase 1: Grouping (O(N))](#phase-1-grouping-on) + - [Phase 2: Sorting (O(K log K))](#phase-2-sorting-ok-log-k) + - [Thread Safety Model](#thread-safety-model) + - [Lock-Free Operations](#lock-free-operations) + - [Fine-Grained Locks](#fine-grained-locks) + - [Pass System Design](#pass-system-design) + - [Why Multi-Pass?](#why-multi-pass) + - [Per-Pass Overrides](#per-pass-overrides) + - [Keyword System Philosophy](#keyword-system-philosophy) + - [Global vs Local](#global-vs-local) + - [Performance Targets](#performance-targets) + - [Microbenchmarks](#microbenchmarks) + - [Real-World Expected](#real-world-expected) + - [Unsafe Code Justification](#unsafe-code-justification) + - [Where & Why](#where-why) + - [Safety Measures](#safety-measures) + - [Extension & Customization Points](#extension-customization-points) + - [1. Custom Property Types](#1-custom-property-types) + - [2. Custom Batching Logic](#2-custom-batching-logic) + - [3. Material Inheritance](#3-material-inheritance) + - [Comparison to Production Engines](#comparison-to-production-engines) + - [Unity URP (Scriptable Render Pipeline)](#unity-urp-scriptable-render-pipeline) + - [Unreal Engine 5](#unreal-engine-5) + - [Godot 4](#godot-4) + - [Future Optimizations](#future-optimizations) + - [1. GPU-Driven Rendering](#1-gpu-driven-rendering) + - [2. Parallel Compilation](#2-parallel-compilation) + - [3. Material LOD](#3-material-lod) + - [4. Texture Streaming](#4-texture-streaming) + - [Conclusion](#conclusion) + + ## Ghost Shader Concept - Technical Deep Dive ### Overview diff --git a/Ghost.Shader.Test/Ghost.Shader.Test.csproj b/Ghost.Shader.Test/Ghost.Shader.Test.csproj index b2c8a6a..480aa0e 100644 --- a/Ghost.Shader.Test/Ghost.Shader.Test.csproj +++ b/Ghost.Shader.Test/Ghost.Shader.Test.csproj @@ -9,7 +9,7 @@ - + diff --git a/Ghost.Shader.Test/Program.cs b/Ghost.Shader.Test/Program.cs index 2fbe3aa..e53ffff 100644 --- a/Ghost.Shader.Test/Program.cs +++ b/Ghost.Shader.Test/Program.cs @@ -1,4 +1,4 @@ -using Ghost.SDL.Compiler; +using Ghost.DSL.ShaderCompiler; using Misaki.HighPerformance.Mathematics; using System.Numerics; diff --git a/Ghost.Shader/BuiltIn/Properties.hlsl b/Ghost.Shader/BuiltIn/Properties.hlsl deleted file mode 100644 index 4b97868..0000000 --- a/Ghost.Shader/BuiltIn/Properties.hlsl +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef BUILTIN_PROPERTIES_HLSL -#define BUILTIN_PROPERTIES_HLSL - -#include "F:/csharp/GhostEngine/Ghost.Shader/BuiltIn/Common.hlsl" - -struct PerViewData -{ - float4x4 cameraMatrix; - float4x4 cameraInverseMatrix; - float4 screenSize; // xy = size, zw = 1/size -}; - -struct PerObjectData -{ - float4x4 localToWorld; - float3 worldBoundsMin; - BYTE_ADDRESS_BUFFER vertexBuffer; - float3 worldBoundsMax; - BYTE_ADDRESS_BUFFER indexBuffer; -}; - -cbuffer GlobalConstants : register(b0) -{ - GlobalData g_GlobalData; -}; - -cbuffer PerViewConstants : register(b1) -{ - PerViewData g_PerViewData; -}; - -cbuffer PerObjectConstants : register(b2) -{ - PerObjectData g_PerObjectData; -}; - -cbuffer PerMaterialConstants : register(b3) -{ - PerMaterialData g_PerMaterialData; -}; - -#if defined(USE_TRADITIONAL_BINDLESS) -Texture2D GlobalTexture2DHeap[GLOBAL_TEXTURE2D_HEAP_SIZE] : register(t0); -Texture3D GlobalTexture3DHeap[GLOBAL_TEXTURE3D_HEAP_SIZE] : register(t0); -TextureCube GlobalTextureCubeHeap[GLOBAL_TEXTURECUBE_HEAP_SIZE] : register(t0); -Texture2DArray GlobalTexture2DArrayHeap[GLOBAL_TEXTURE2D_ARRAY_HEAP_SIZE] : register(t0); -TextureCubeArray GlobalTextureCubeArrayHeap[GLOBAL_TEXTURECUBE_ARRAY_HEAP_SIZE] : register(t0); -SamplerState GlobalSamplerHeap[GLOBAL_SAMPLER_HEAP_SIZE] : register(s0); -#endif - -#endif // BUILTIN_PROPERTIES_HLSL \ No newline at end of file diff --git a/GhostEngine.slnx b/GhostEngine.slnx index a28bcd3..656a84e 100644 --- a/GhostEngine.slnx +++ b/GhostEngine.slnx @@ -38,5 +38,5 @@ - +