godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
Ignacio Roldán Etcheverry f744d99179 C#: Restructure code prior move to .NET Core
The main focus here was to remove the majority of code that relied on
Mono's embedding APIs, specially the reflection APIs. The embedding
APIs we still use are the bare minimum we need for things to work.
A lot of code was moved to C#. We no longer deal with any managed
objects (`MonoObject*`, and such) in native code, and all marshaling
is done in C#.

The reason for restructuring the code and move away from embedding APIs
is that once we move to .NET Core, we will be limited by the much more
minimal .NET hosting.

PERFORMANCE REGRESSIONS
-----------------------

Some parts of the code were written with little to no concern about
performance. This includes code that calls into script methods and
accesses script fields, properties and events.
The reason for this is that all of that will be moved to source
generators, so any work prior to that would be a waste of time.

DISABLED FEATURES
-----------------

Some code was removed as it no longer makes sense (or won't make sense
in the future).
Other parts were commented out with `#if 0`s and TODO warnings because
it doesn't make much sense to work on them yet as those parts will
change heavily when we switch to .NET Core but also when we start
introducing source generators.
As such, the following features were disabled temporarily:
- Assembly-reloading (will be done with ALCs in .NET Core).
- Properties/fields exports and script method listing (will be
  handled by source generators in the future).
- Exception logging in the editor and stack info for errors.
- Exporting games.
- Building of C# projects. We no longer copy the Godot API assemblies
  to the project directory, so MSBuild won't be able to find them. The
  idea is to turn them into NuGet packages in the future, which could
  also be obtained from local NuGet sources during development.
2021-09-22 06:38:00 +02:00

472 lines
15 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
namespace Godot.NativeInterop
{
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_bool
{
public byte _value;
public unsafe godot_bool(bool value) => _value = *(byte*)&value;
public static unsafe implicit operator bool(godot_bool godotBool) => *(bool*)&godotBool._value;
public static implicit operator godot_bool(bool @bool) => new godot_bool(@bool);
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_ref : IDisposable
{
internal IntPtr _reference;
public void Dispose()
{
if (_reference == IntPtr.Zero)
return;
NativeFuncs.godotsharp_ref_destroy(ref this);
_reference = IntPtr.Zero;
}
public bool IsNull => _reference == IntPtr.Zero;
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum godot_variant_call_error_error
{
GODOT_CALL_ERROR_CALL_OK = 0,
GODOT_CALL_ERROR_CALL_ERROR_INVALID_METHOD,
GODOT_CALL_ERROR_CALL_ERROR_INVALID_ARGUMENT,
GODOT_CALL_ERROR_CALL_ERROR_TOO_MANY_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_TOO_FEW_ARGUMENTS,
GODOT_CALL_ERROR_CALL_ERROR_INSTANCE_IS_NULL,
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_variant_call_error
{
public godot_variant_call_error_error error;
public int argument;
public Godot.Variant.Type expected;
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
public struct godot_variant : IDisposable
{
[FieldOffset(0)] public Godot.Variant.Type _type;
// There's padding here
[FieldOffset(8)] internal godot_variant_data _data;
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
internal unsafe struct godot_variant_data
{
[FieldOffset(0)] public godot_bool _bool;
[FieldOffset(0)] public long _int;
[FieldOffset(0)] public double _float;
[FieldOffset(0)] public Transform2D* _transform2d;
[FieldOffset(0)] public AABB* _aabb;
[FieldOffset(0)] public Basis* _basis;
[FieldOffset(0)] public Transform3D* _transform3d;
[FieldOffset(0)] private godot_variant_data_mem _mem;
// The following fields are not in the C++ union, but this is how they're stored in _mem.
[FieldOffset(0)] public godot_string_name _m_string_name;
[FieldOffset(0)] public godot_string _m_string;
[FieldOffset(0)] public Vector3 _m_vector3;
[FieldOffset(0)] public Vector3i _m_vector3i;
[FieldOffset(0)] public Vector2 _m_vector2;
[FieldOffset(0)] public Vector2i _m_vector2i;
[FieldOffset(0)] public Rect2 _m_rect2;
[FieldOffset(0)] public Rect2i _m_rect2i;
[FieldOffset(0)] public Plane _m_plane;
[FieldOffset(0)] public Quaternion _m_quaternion;
[FieldOffset(0)] public Color _m_color;
[FieldOffset(0)] public godot_node_path _m_node_path;
[FieldOffset(0)] public RID _m_rid;
[FieldOffset(0)] public godot_variant_obj_data _m_obj_data;
[FieldOffset(0)] public godot_callable _m_callable;
[FieldOffset(0)] public godot_signal _m_signal;
[FieldOffset(0)] public godot_dictionary _m_dictionary;
[FieldOffset(0)] public godot_array _m_array;
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
internal struct godot_variant_obj_data
{
public UInt64 id;
public IntPtr obj;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
private struct godot_variant_data_mem
{
#pragma warning disable 169
private real_t _mem0;
private real_t _mem1;
private real_t _mem2;
private real_t _mem3;
#pragma warning restore 169
}
}
public void Dispose()
{
switch (_type)
{
case Variant.Type.Nil:
case Variant.Type.Bool:
case Variant.Type.Int:
case Variant.Type.Float:
case Variant.Type.Vector2:
case Variant.Type.Vector2i:
case Variant.Type.Rect2:
case Variant.Type.Rect2i:
case Variant.Type.Vector3:
case Variant.Type.Vector3i:
case Variant.Type.Plane:
case Variant.Type.Quaternion:
case Variant.Type.Color:
case Variant.Type.Rid:
return;
}
NativeFuncs.godotsharp_variant_destroy(ref this);
_type = Variant.Type.Nil;
}
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_string : IDisposable
{
internal IntPtr _ptr;
public void Dispose()
{
if (_ptr == IntPtr.Zero)
return;
NativeFuncs.godotsharp_string_destroy(ref this);
_ptr = IntPtr.Zero;
}
// Size including the null termination character
public unsafe int Size => _ptr != IntPtr.Zero ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_string_name : IDisposable
{
internal IntPtr _data;
public void Dispose()
{
if (_data == IntPtr.Zero)
return;
NativeFuncs.godotsharp_string_name_destroy(ref this);
_data = IntPtr.Zero;
}
// An static method because an instance method could result in a hidden copy if called on an `in` parameter.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEmpty(in godot_string_name name) =>
// This is all that's needed to check if it's empty. Equivalent to `== StringName()` in C++.
name._data == IntPtr.Zero;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_node_path : IDisposable
{
internal IntPtr _data;
public void Dispose()
{
if (_data == IntPtr.Zero)
return;
NativeFuncs.godotsharp_node_path_destroy(ref this);
_data = IntPtr.Zero;
}
// An static method because an instance method could result in a hidden copy if called on an `in` parameter.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsEmpty(in godot_node_path nodePath) =>
// This is all that's needed to check if it's empty. It's what the `is_empty()` C++ method does.
nodePath._data == IntPtr.Zero;
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
public struct godot_signal : IDisposable
{
[FieldOffset(0)] public godot_string_name _name;
// There's padding here on 32-bit
[FieldOffset(8)] public UInt64 _objectId;
public void Dispose()
{
if (_name._data == IntPtr.Zero)
return;
NativeFuncs.godotsharp_signal_destroy(ref this);
_name._data = IntPtr.Zero;
}
}
[StructLayout(LayoutKind.Explicit)]
// ReSharper disable once InconsistentNaming
public struct godot_callable : IDisposable
{
[FieldOffset(0)] public godot_string_name _method;
// There's padding here on 32-bit
[FieldOffset(8)] public UInt64 _objectId;
[FieldOffset(8)] public IntPtr _custom;
public void Dispose()
{
if (_method._data == IntPtr.Zero && _custom == IntPtr.Zero)
return;
NativeFuncs.godotsharp_callable_destroy(ref this);
_method._data = IntPtr.Zero;
_custom = IntPtr.Zero;
}
}
// A correctly constructed value needs to call the native default constructor to allocate `_p`.
// Don't pass a C# default constructed `godot_array` to native code, unless it's going to
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_array : IDisposable
{
internal unsafe ArrayPrivate* _p;
[StructLayout(LayoutKind.Sequential)]
internal struct ArrayPrivate
{
private uint _safeRefCount;
internal VariantVector _arrayVector;
// There's more here, but we don't care as we never store this in C#
}
[StructLayout(LayoutKind.Sequential)]
internal struct VariantVector
{
internal IntPtr _writeProxy;
internal unsafe godot_variant* _ptr;
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
public unsafe int Size => _p != null ? _p->_arrayVector.Size : 0;
public unsafe void Dispose()
{
if (_p == null)
return;
NativeFuncs.godotsharp_array_destroy(ref this);
_p = null;
}
}
// IMPORTANT:
// A correctly constructed value needs to call the native default constructor to allocate `_p`.
// Don't pass a C# default constructed `godot_dictionary` to native code, unless it's going to
// be re-assigned a new value (the copy constructor checks if `_p` is null so that's fine).
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_dictionary : IDisposable
{
internal IntPtr _p;
public void Dispose()
{
if (_p == IntPtr.Zero)
return;
NativeFuncs.godotsharp_dictionary_destroy(ref this);
_p = IntPtr.Zero;
}
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_byte_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe byte* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_byte_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_int32_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe int* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_int32_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *(_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_int64_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe long* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_int64_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_float32_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe float* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_float32_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_float64_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe double* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_float64_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_string_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe godot_string* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_string_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_vector2_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Vector2* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_vector2_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_vector3_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Vector3* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_vector3_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct godot_packed_color_array : IDisposable
{
internal IntPtr _writeProxy;
internal unsafe Color* _ptr;
public unsafe void Dispose()
{
if (_ptr == null)
return;
NativeFuncs.godotsharp_packed_color_array_destroy(ref this);
_ptr = null;
}
public unsafe int Size => _ptr != null ? *((int*)_ptr - 1) : 0;
}
}