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

AreAllMembersBlittable throws TypeLoadException in NativeAOT #51

Open
cNoNim opened this issue Jun 16, 2024 · 9 comments
Open

AreAllMembersBlittable throws TypeLoadException in NativeAOT #51

cNoNim opened this issue Jun 16, 2024 · 9 comments
Assignees

Comments

@cNoNim
Copy link

cNoNim commented Jun 16, 2024

If a registered component has an enum, AreAllMembersBlittable throws a TypeLoadException in NativeAOT.

public enum StatusEffect
{
    Spawn,
    Dead,
    Alive
}

public struct Health : IComponent
{
    public int Hp;

    public int MaxHp;

    public StatusEffect Status;
}
System.TypeLoadException: The type 'Benchmark.Tests.Components.StatusEffect' cannot be found in assembly 'Benchmark.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at System.Reflection.Runtime.General.TypeResolver.Resolve(Handle, MetadataReader, TypeContext) + 0x29
   at System.Reflection.Runtime.FieldInfos.NativeFormat.NativeFormatRuntimeFieldInfo.get_FieldRuntimeType() + 0x51
   at System.Reflection.Runtime.FieldInfos.RuntimeFieldInfo.get_FieldType() + 0x1e
   at Friflo.Engine.ECS.SchemaType.AreAllMembersBlittable(Type) + 0x77
   at Friflo.Engine.ECS.SchemaType.GetBlittableType(Type) + 0xa3
   at Friflo.Engine.ECS.ComponentType`1..ctor(String, Int32, TypeStore) + 0x41
   at Friflo.Engine.ECS.SchemaUtils.CreateComponentType[T](TypeStore typeStore, Int32 structIndex) + 0x40
   at Friflo.Engine.ECS.NativeAOT.RegisterComponent[T]() + 0x5e
   at Benchmark.Tests.Mix.MixFrifloContext..ctor(Int32) + 0x8d
   at Benchmark.Tests.Mix.Mix.FrifloSetup() + 0x23
   at BenchmarkDotNet.Engines.EngineFactory.CreateReadyToRun(EngineParameters) + 0x7e
   at BenchmarkDotNet.Autogenerated.Runnable_103.Run(IHost, String) + 0x576
   at BenchmarkDotNet.Autogenerated.UniqueProgramName.AfterAssemblyLoadingAttached(String[]) + 0x9b4
@friflo
Copy link
Owner

friflo commented Jun 16, 2024

hi,

It tried to reproduce the exception without success in a small test.
One question.

Did you register the Health component with

aot.RegisterComponent<Health>();

as mentioned at Examples - General > Native AOT?

edit: Ah, you did. NativeAOT.RegisterComponent[T] is part of the stack trace.

@cNoNim
Copy link
Author

cNoNim commented Jun 16, 2024

Yes.
I fixed the component and the error is gone.
But it's a crutch.

public struct Health : IComponent, IComponentData
{
    public int Hp;

    public int MaxHp;

    private int _status;

    public StatusEffect Status
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => (StatusEffect)_status;
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        set => _status = (int)value;
    }
}

@friflo
Copy link
Owner

friflo commented Jun 16, 2024

I assume you are using .NET 8?
I am using: \Program Files\dotnet\sdk\8.0.202

@friflo
Copy link
Owner

friflo commented Jun 16, 2024

According to this post:
https://stackoverflow.com/questions/77776702/net8-nativeaot-type-getproperties-doesnt-work-for-some-types-but-does-for-ot

Can you try adding the attribute [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)]
to the struct.

I assume StatusEffect Status is not used anywhere in your assembly. right?

@cNoNim
Copy link
Author

cNoNim commented Jun 17, 2024

Used, of course.

And DynamicallyAccessedMembers didn't help.

I can try to make a reproducible project, but it will take time.

@friflo
Copy link
Owner

friflo commented Jun 17, 2024

I can try to make a reproducible project, but it will take time.

That would be very helpful!

The workaround using a property - like public StatusEffect Status { get; set; }
is a crutch.

@cNoNim
Copy link
Author

cNoNim commented Jun 18, 2024

I've tried to find the problem, but it's not in the framework. BenchmarkDotNet doesn't compile the NativeAOT build correctly if you run benchmarks selectively. The problem is definitely a floating issue.

@cNoNim
Copy link
Author

cNoNim commented Jun 18, 2024

Another question came up. Should we create an open issue?

I'm not comfortable adding the IComponent/ITag interface to all components and tags in my tests.

I tried to bypass the system as follows:

public struct Comp<T>(T v) : IComponent
{
    public T V = v;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static implicit operator Comp<T>(in T value) =>
        new() { V = value };
}

public struct Tag<T> : ITag
{
    public T Value;
}

But it wasn't good.

Test method Benchmark.Mix.Tests.HashTests.Test threw exception: 
System.TypeInitializationException: The type initializer for 'Friflo.Engine.ECS.StructInfo`1' threw an exception. ---> System.Collections.Generic.KeyNotFoundException: The given key 'Benchmark.Core.Friflo.Comp`1[Benchmark.Core.Components.Velocity]' was not present in the dictionary.
    at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Friflo.Engine.ECS.SchemaTypeUtils.GetStructIndex(Type type)
   at Friflo.Engine.ECS.StructInfo`1..cctor()
--- End of inner exception stack trace ---
    at Friflo.Engine.ECS.ComponentTypes.Get[T1,T2,T3,T4]()
   at Friflo.Engine.ECS.Systems.QuerySystem`4..ctor()
   at Benchmark.Mix.MixContextFriflo.UpdateVelocitySystem..ctor()
   at Benchmark.Mix.MixContextFriflo.DoSetup() in E:\Projects\ECS.Net\Benchmark.Mix\MixContextFriflo.cs:line 77
   at Benchmark.Mix.MixContextBase.Setup(Int32 entityCount, Int32 ticks) in E:\Projects\ECS.Net\Benchmark.Mix\MixContextBase.cs:line 165
   at Benchmark.Mix.Tests.HashTests.TestContext(IMixContext context, Nullable`1& hash, Int32 entityCount, Int32 ticks) in E:\Projects\ECS.Net\Benchmark.Mix.Tests\HashTests.cs:line 62
   at Benchmark.Mix.Tests.HashTests.TestContexts(IMixContext[] contexts, Int32 entityCount, Int32 ticks) in E:\Projects\ECS.Net\Benchmark.Mix.Tests\HashTests.cs:line 43
   at Benchmark.Mix.Tests.HashTests.Test(Int32 entityCount, Int32 ticks) in E:\Projects\ECS.Net\Benchmark.Mix.Tests\HashTests.cs:line 22
   at InvokeStub_HashTests.Test(Object, Span`1)
   at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

The problem doesn't involve NativeAot. Friflo.Engine.ECS.AssemblyLoader.GetComponentTypes doesn't bypass generic types. It can't be done automatically, but maybe I can add types manually?

@friflo
Copy link
Owner

friflo commented Jun 19, 2024

Another question came up. Should we create an open issue?

Thx for reporting this.
I created an new issue: #53

@friflo friflo self-assigned this Jun 19, 2024
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