Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v4] Implement component id lookup support for System.Type #27

Open
BeanCheeseBurrito opened this issue Jun 21, 2024 · 6 comments
Open

Comments

@BeanCheeseBurrito
Copy link
Owner

It would be useful in reflection scenarios to allow the user to lookup component ids and execute ECS operations using System.Type. This functionality is required for automatic member registration/serialization/deserialization support. Needs to also support NativeAOT with trimming which might not be possible with the way component registration currently works.

// Example of registering all types in an assembly through reflection.
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
    world.Component(type);
@xentripetal
Copy link
Contributor

I messed around with trying to implement this and it seems like there's no way to get the size of the component without breaking NativeAOT.

You can construct an instance of T using Activator as long as you annotate the type properly. However, there's no way to size it directly since you can't access Marshal.SizeOf in AoT.

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]

You might be able to hack some sort size estimator by walking the fields and using known sizes for base types but that would get very messy.

Might be out of scope but you could consider splitting into a Flecs.Core package with AoT support and a Flecs package without that adds support for dynamic registration and types. Alternatively you could use defines for gating off non-aot compatible code.

@xentripetal
Copy link
Contributor

xentripetal commented Aug 3, 2024

Worth noting, it seems Type currently isn't AoT compatible. Its usage of GetEnumValues throws an AoT warning.

Adding <PublishAoT>true</PublishAoT> to the Examples project and running

dotnet publish --property:Example=Reflection_BasicsEnum -r osx-arm64
./bin/Release/net8.0/osx-arm64/publish/Flecs.NET.Examples
Unhandled Exception: System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property.
 ---> System.NotSupportedException: '<BasicsEnum>FD8BE5548CB1ACE8A8CBC7E39E2FB23FDF52B13BA421B001B7B83140A7A1DF813__Color[]' is missing native code or metadata. This can happen for code that is not compatible with trimming or AOT. Inspect and fix trimming and AOT related warnings that were generated when the app was published. For more information see https://aka.ms/nativeaot-compatibility
   at System.Reflection.Runtime.General.TypeUnifier.WithVerifiedTypeHandle(RuntimeArrayTypeInfo, RuntimeTypeInfo) + 0x70
   at System.Array.InternalCreate(RuntimeType, Int32, Int32*, Int32*) + 0x78
   at System.Array.CreateInstance(Type, Int32) + 0x48
   at System.RuntimeType.GetEnumValues() + 0x64
   at Flecs.NET.Core.Type`1.InitEnumCacheIndexes() + 0x64
   at Flecs.NET.Core.Type`1..cctor() + 0x128
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0xbc
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.ClassConstructorRunner.EnsureClassConstructorRun(StaticClassConstructionContext*) + 0x15c
   at System.Runtime.CompilerServices.ClassConstructorRunner.CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext*, IntPtr) + 0x14
   at Flecs.NET.Core.Component`1..ctor(flecs.ecs_world_t*) + 0xa4
   at Reflection_BasicsEnum.Main() + 0x44
   at Flecs.NET!<BaseAddress>+0x171fb4

@BeanCheeseBurrito
Copy link
Owner Author

Worth noting, it seems Type currently isn't AoT compatible. Its usage of GetEnumValues throws an AoT warning.

I wonder if switching to Enum.GetValuesAsUnderlyingType fixes AOT compatibility. I dropped support for versions below .NET 8 in the type-safe-queries branch so we have access to this function now.

@xentripetal
Copy link
Contributor

Yup that fixes it! AoT still gives off a warning about the

            if (RuntimeFeature.IsDynamicCodeSupported)
            {
                FieldInfo[] fields =
                    type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                size = fields.Length == 0 ? 0 : size;
                alignment = size == 0 ? 0 : alignment;
            }

but seems fine since its gated. You could annotate it but it looks like you'd have to bubble up that annotation to 50+ other generic methods that call Type<T>

@xentripetal
Copy link
Contributor

In the meantime, would you have any qualms with storing the reflection Type on the component entity? I have a usecase for going from the component entity Id to knowing its type for runtime reflection.

I figure easiest solution would be to just do

world.Entity(component).Set(typeof(T));

in Type::RegisterComponent

@xentripetal
Copy link
Contributor

See #41

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants