Merge remote-tracking branch 'origin/1.5' into dotnet7

This commit is contained in:
Wojtek Figat
2023-01-08 15:05:56 +01:00
14 changed files with 275 additions and 91 deletions

View File

@@ -144,11 +144,11 @@ public:
/// <returns>The reference to this.</returns> /// <returns>The reference to this.</returns>
Array& operator=(std::initializer_list<T> initList) noexcept Array& operator=(std::initializer_list<T> initList) noexcept
{ {
Memory::DestructItems(_allocation.Get(), _count); Clear();
_count = _capacity = (int32)initList.size(); if (initList.size() > 0)
if (_capacity > 0)
{ {
_allocation.Allocate(_capacity); EnsureCapacity((int32)initList.size());
_count = (int32)initList.size();
Memory::ConstructItems(_allocation.Get(), initList.begin(), _count); Memory::ConstructItems(_allocation.Get(), initList.begin(), _count);
} }
return *this; return *this;

View File

@@ -43,6 +43,15 @@ public:
{ {
return GetEnabled(); return GetEnabled();
} }
/// <summary>
/// Gets a value indicating whether this effect can be rendered.
/// </summary>
/// <param name="renderContext">The target render context.</param>
API_FUNCTION() virtual bool CanRender(const RenderContext& renderContext) const
{
return CanRender();
}
/// <summary> /// <summary>
/// Pre-rendering event called before scene rendering begin. Can be used to perform custom rendering or customize render view/setup. /// Pre-rendering event called before scene rendering begin. Can be used to perform custom rendering or customize render view/setup.

View File

@@ -238,13 +238,13 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch,
{ {
for (PostProcessEffect* fx : GlobalCustomPostFx) for (PostProcessEffect* fx : GlobalCustomPostFx)
{ {
if (fx && fx->CanRender()) if (fx && fx->CanRender(renderContext))
postFx.Add(fx); postFx.Add(fx);
} }
} }
for (PostProcessEffect* fx : CustomPostFx) for (PostProcessEffect* fx : CustomPostFx)
{ {
if (fx && fx->CanRender()) if (fx && fx->CanRender(renderContext))
postFx.Add(fx); postFx.Add(fx);
} }
if (const auto* camera = Camera.Get()) if (const auto* camera = Camera.Get())
@@ -252,7 +252,7 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch,
for (Script* script : camera->Scripts) for (Script* script : camera->Scripts)
{ {
auto* fx = Cast<PostProcessEffect>(script); auto* fx = Cast<PostProcessEffect>(script);
if (fx && fx->CanRender()) if (fx && fx->CanRender(renderContext))
postFx.Add(fx); postFx.Add(fx);
} }
} }

View File

@@ -107,6 +107,9 @@ void PluginManagerService::InvokeInitialize(Plugin* plugin)
{ {
if (plugin->_initialized) if (plugin->_initialized)
return; return;
StringAnsiView typeName = plugin->GetType().GetName();
PROFILE_CPU();
ZoneName(typeName.Get(), typeName.Length());
LOG(Info, "Loading plugin {}", plugin->ToString()); LOG(Info, "Loading plugin {}", plugin->ToString());
@@ -122,6 +125,9 @@ void PluginManagerService::InvokeDeinitialize(Plugin* plugin)
{ {
if (!plugin->_initialized) if (!plugin->_initialized)
return; return;
StringAnsiView typeName = plugin->GetType().GetName();
PROFILE_CPU();
ZoneName(typeName.Get(), typeName.Length())
LOG(Info, "Unloading plugin {}", plugin->ToString()); LOG(Info, "Unloading plugin {}", plugin->ToString());

View File

@@ -742,6 +742,23 @@ ScriptingTypeHandle Scripting::FindScriptingType(const StringAnsiView& fullname)
return ScriptingTypeHandle(); return ScriptingTypeHandle();
} }
ScriptingObject* Scripting::NewObject(const ScriptingTypeHandle& type)
{
if (!type)
{
LOG(Error, "Invalid type.");
return nullptr;
}
const ScriptingType& scriptingType = type.GetType();
// Create unmanaged object
const ScriptingObjectSpawnParams params(Guid::New(), type);
ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr)
LOG(Error, "Failed to spawn object of type \'{0}\'.", scriptingType.ToString());
return obj;
}
ScriptingObject* Scripting::NewObject(const MClass* type) ScriptingObject* Scripting::NewObject(const MClass* type)
{ {
if (type == nullptr) if (type == nullptr)
@@ -772,10 +789,7 @@ ScriptingObject* Scripting::NewObject(const MClass* type)
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex)); const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
ScriptingObject* obj = scriptingType.Script.Spawn(params); ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr) if (obj == nullptr)
{ LOG(Error, "Failed to spawn object of type \'{0}\'.", scriptingType.ToString());
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
return obj; return obj;
#else #else
LOG(Error, "Not supported object creation from Managed class."); LOG(Error, "Not supported object creation from Managed class.");

View File

@@ -109,6 +109,13 @@ public:
/// <returns>The scripting type or invalid type if missing.</returns> /// <returns>The scripting type or invalid type if missing.</returns>
static ScriptingTypeHandle FindScriptingType(const StringAnsiView& fullname); static ScriptingTypeHandle FindScriptingType(const StringAnsiView& fullname);
/// <summary>
/// Creates a new instance of the given type object (native construction).
/// </summary>
/// <param name="type">The scripting object type class.</param>
/// <returns>The created object or null if failed.</returns>
static ScriptingObject* NewObject(const ScriptingTypeHandle& type);
/// <summary> /// <summary>
/// Creates a new instance of the given class object (native construction). /// Creates a new instance of the given class object (native construction).
/// </summary> /// </summary>

View File

@@ -16,20 +16,25 @@ TEST_CASE("Scripting")
// Test native class // Test native class
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassNative"); ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassNative");
CHECK(type == TestClassNative::TypeInitializer); CHECK(type == TestClassNative::TypeInitializer);
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass); ScriptingObject* object = Scripting::NewObject(type);
CHECK(object); CHECK(object);
CHECK(object->Is<TestClassNative>()); CHECK(object->Is<TestClassNative>());
TestClassNative* testClass = (TestClassNative*)object; TestClassNative* testClass = (TestClassNative*)object;
CHECK(testClass->SimpleField == 1); CHECK(testClass->SimpleField == 1);
CHECK(testClass->SimpleStruct.Object == nullptr); CHECK(testClass->SimpleStruct.Object == nullptr);
CHECK(testClass->SimpleStruct.Vector == Float3::One); CHECK(testClass->SimpleStruct.Vector == Float3::One);
int32 methodResult = testClass->TestMethod(TEXT("123")); Array<TestStruct> struct1 = { testClass->SimpleStruct };
Array<TestStruct> struct2 = { testClass->SimpleStruct };
Array<ScriptingObject*> objects;
TestStructPOD pod;
int32 methodResult = testClass->TestMethod(TEXT("123"), pod, struct1, struct2, objects);
CHECK(methodResult == 3); CHECK(methodResult == 3);
CHECK(objects.Count() == 0);
// Test managed class // Test managed class
type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged"); type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
CHECK(type); CHECK(type);
object = Scripting::NewObject(type.GetType().ManagedClass); object = Scripting::NewObject(type);
CHECK(object); CHECK(object);
CHECK(object->Is<TestClassNative>()); CHECK(object->Is<TestClassNative>());
testClass = (TestClassNative*)object; testClass = (TestClassNative*)object;
@@ -38,15 +43,24 @@ TEST_CASE("Scripting")
CHECK(testClass->SimpleField == 2); CHECK(testClass->SimpleField == 2);
CHECK(testClass->SimpleStruct.Object == testClass); CHECK(testClass->SimpleStruct.Object == testClass);
CHECK(testClass->SimpleStruct.Vector == Float3::UnitX); CHECK(testClass->SimpleStruct.Vector == Float3::UnitX);
methodResult = testClass->TestMethod(TEXT("123")); struct1 = { testClass->SimpleStruct };
struct2 = { testClass->SimpleStruct };
objects.Clear();
pod.Vector = Float3::One;
methodResult = testClass->TestMethod(TEXT("123"), pod, struct1, struct2, objects);
CHECK(methodResult == 6); CHECK(methodResult == 6);
CHECK(pod.Vector == Float3::Half);
CHECK(struct2.Count() == 2);
CHECK(struct2[0] == testClass->SimpleStruct);
CHECK(struct2[1] == testClass->SimpleStruct);
CHECK(objects.Count() == 3);
} }
SECTION("Test Event") SECTION("Test Event")
{ {
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged"); ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
CHECK(type); CHECK(type);
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass); ScriptingObject* object = Scripting::NewObject(type);
CHECK(object); CHECK(object);
MObject* managed = object->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor MObject* managed = object->GetOrCreateManagedInstance(); // Ensure to create C# object and run it's ctor
CHECK(managed); CHECK(managed);
@@ -65,12 +79,13 @@ TEST_CASE("Scripting")
CHECK(arr2[1].Vector == testClass->SimpleStruct.Vector); CHECK(arr2[1].Vector == testClass->SimpleStruct.Vector);
CHECK(arr2[1].Object == testClass); CHECK(arr2[1].Object == testClass);
} }
SECTION("Test Interface") SECTION("Test Interface")
{ {
// Test native interface implementation // Test native interface implementation
ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassNative"); ScriptingTypeHandle type = Scripting::FindScriptingType("FlaxEngine.TestClassNative");
CHECK(type); CHECK(type);
ScriptingObject* object = Scripting::NewObject(type.GetType().ManagedClass); ScriptingObject* object = Scripting::NewObject(type);
CHECK(object); CHECK(object);
TestClassNative* testClass = (TestClassNative*)object; TestClassNative* testClass = (TestClassNative*)object;
int32 methodResult = testClass->TestInterfaceMethod(TEXT("123")); int32 methodResult = testClass->TestInterfaceMethod(TEXT("123"));
@@ -86,7 +101,7 @@ TEST_CASE("Scripting")
// Test managed interface override // Test managed interface override
type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged"); type = Scripting::FindScriptingType("FlaxEngine.TestClassManaged");
CHECK(type); CHECK(type);
object = Scripting::NewObject(type.GetType().ManagedClass); object = Scripting::NewObject(type);
CHECK(object); CHECK(object);
testClass = (TestClassNative*)object; testClass = (TestClassNative*)object;
methodResult = testClass->TestInterfaceMethod(TEXT("123")); methodResult = testClass->TestInterfaceMethod(TEXT("123"));
@@ -102,7 +117,7 @@ TEST_CASE("Scripting")
// Test managed interface implementation // Test managed interface implementation
type = Scripting::FindScriptingType("FlaxEngine.TestInterfaceManaged"); type = Scripting::FindScriptingType("FlaxEngine.TestInterfaceManaged");
CHECK(type); CHECK(type);
object = Scripting::NewObject(type.GetType().ManagedClass); object = Scripting::NewObject(type);
CHECK(object); CHECK(object);
interface = ScriptingObject::ToInterface<ITestInterface>(object); interface = ScriptingObject::ToInterface<ITestInterface>(object);
CHECK(interface); CHECK(interface);

View File

@@ -63,10 +63,24 @@ namespace FlaxEngine
} }
/// <inheritdoc /> /// <inheritdoc />
public override int TestMethod(string str) public override int TestMethod(string str, ref TestStructPOD pod, TestStruct[] struct1, ref TestStruct[] struct2, out Object[] objects)
{ {
objects = new Object[3];
if (struct1 == null || struct1.Length != 1)
return -1;
if (struct2 == null || struct2.Length != 1)
return -2;
if (pod.Vector != Float3.One)
return -3;
struct2 = new TestStruct[2]
{
struct1[0],
SimpleStruct,
};
pod.Vector = Float3.Half;
// Test C++ base method invocation // Test C++ base method invocation
return str.Length + base.TestMethod(str); return str.Length + base.TestMethod(str, ref pod, struct1, ref struct2, out _);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -17,6 +17,26 @@ API_STRUCT(NoDefault) struct TestStruct : public ISerializable
API_FIELD() Float3 Vector = Float3::One; API_FIELD() Float3 Vector = Float3::One;
// Ref // Ref
API_FIELD() ScriptingObject* Object = nullptr; API_FIELD() ScriptingObject* Object = nullptr;
friend bool operator==(const TestStruct& lhs, const TestStruct& rhs)
{
return lhs.Vector == rhs.Vector && lhs.Object == rhs.Object;
}
};
// Test structure.
API_STRUCT(NoDefault) struct TestStructPOD
{
DECLARE_SCRIPTING_TYPE_MINIMAL(TestStructPOD);
// Var
API_FIELD() Float3 Vector = Float3::One;
};
template<>
struct TIsPODType<TestStructPOD>
{
enum { Value = true };
}; };
// Test interface. // Test interface.
@@ -46,7 +66,7 @@ public:
API_EVENT() Delegate<int32, Float3, const String&, String&, const Array<TestStruct>&, Array<TestStruct>&> SimpleEvent; API_EVENT() Delegate<int32, Float3, const String&, String&, const Array<TestStruct>&, Array<TestStruct>&> SimpleEvent;
// Test virtual method // Test virtual method
API_FUNCTION() virtual int32 TestMethod(const String& str) API_FUNCTION() virtual int32 TestMethod(const String& str, API_PARAM(Ref) TestStructPOD& pod, const Array<TestStruct>& struct1, API_PARAM(Ref) Array<TestStruct>& struct2, API_PARAM(Out) Array<ScriptingObject*>& objects)
{ {
return str.Length(); return str.Length();
} }

View File

@@ -274,6 +274,16 @@ namespace Flax.Build.Bindings
// Find across in-build types // Find across in-build types
if (InBuildTypes.TryGetValue(typeInfo, out result)) if (InBuildTypes.TryGetValue(typeInfo, out result))
return result; return result;
if (typeInfo.IsRef)
{
typeInfo.IsRef = false;
if (InBuildTypes.TryGetValue(typeInfo, out result))
{
typeInfo.IsRef = true;
return result;
}
typeInfo.IsRef = true;
}
// Find across all loaded modules for this build // Find across all loaded modules for this build
foreach (var e in buildData.ModulesInfo) foreach (var e in buildData.ModulesInfo)

View File

@@ -99,7 +99,7 @@ namespace Flax.Build.Bindings
return sb.ToString(); return sb.ToString();
} }
private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isOut, out bool useLocalVar) private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isRef, out bool useLocalVar)
{ {
useLocalVar = false; useLocalVar = false;
var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null); var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null);
@@ -107,7 +107,7 @@ namespace Flax.Build.Bindings
if (!string.IsNullOrEmpty(nativeToManaged)) if (!string.IsNullOrEmpty(nativeToManaged))
{ {
result = string.Format(nativeToManaged, paramName); result = string.Format(nativeToManaged, paramName);
if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isOut) if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isRef)
{ {
// Pass pointer value // Pass pointer value
} }
@@ -124,7 +124,7 @@ namespace Flax.Build.Bindings
else else
{ {
result = paramName; result = paramName;
if (paramType.IsRef && !paramType.IsConst && !isOut) if (paramType.IsRef && !paramType.IsConst && !isRef)
{ {
// Pass reference as a pointer // Pass reference as a pointer
result = '&' + result; result = '&' + result;
@@ -158,13 +158,13 @@ namespace Flax.Build.Bindings
{ {
var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo); var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo);
CppVariantFromTypes[wrapperName] = typeInfo; CppVariantFromTypes[wrapperName] = typeInfo;
return $"VariantFrom{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}Array({value}, {typeInfo.ArraySize})"; return $"VariantFrom{GenerateCppWrapperNativeToVariantMethodName(typeInfo)}Array((const {typeInfo}*){value}, {typeInfo.ArraySize})";
} }
if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null) if (typeInfo.Type == "Array" && typeInfo.GenericArgs != null)
{ {
var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo.GenericArgs[0]); var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo.GenericArgs[0]);
CppVariantFromTypes[wrapperName] = typeInfo; CppVariantFromTypes[wrapperName] = typeInfo;
return $"VariantFrom{wrapperName}Array({value}.Get(), {value}.Count())"; return $"VariantFrom{wrapperName}Array((const {typeInfo.GenericArgs[0]}*){value}.Get(), {value}.Count())";
} }
if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null) if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null)
{ {
@@ -826,10 +826,10 @@ namespace Flax.Build.Bindings
} }
} }
private static string GenerateCppWrapperNativeToBox(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, string value) private static string GenerateCppWrapperNativeToBox(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, out ApiTypeInfo apiType, string value)
{ {
// Optimize passing scripting objects // Optimize passing scripting objects
var apiType = FindApiTypeInfo(buildData, typeInfo, caller); apiType = FindApiTypeInfo(buildData, typeInfo, caller);
if (apiType != null && apiType.IsScriptingObject) if (apiType != null && apiType.IsScriptingObject)
return $"ScriptingObject::ToManaged((ScriptingObject*){value})"; return $"ScriptingObject::ToManaged((ScriptingObject*){value})";
@@ -1291,6 +1291,8 @@ namespace Flax.Build.Bindings
contents.Append(')'); contents.Append(')');
contents.AppendLine(); contents.AppendLine();
contents.AppendLine(" {"); contents.AppendLine(" {");
// Get object
string scriptVTableOffset; string scriptVTableOffset;
if (classInfo.IsInterface) if (classInfo.IsInterface)
{ {
@@ -1310,6 +1312,7 @@ namespace Flax.Build.Bindings
} }
contents.AppendLine(" static THREADLOCAL void* WrapperCallInstance = nullptr;"); contents.AppendLine(" static THREADLOCAL void* WrapperCallInstance = nullptr;");
// Base method call
contents.AppendLine(" ScriptingTypeHandle managedTypeHandle = object->GetTypeHandle();"); contents.AppendLine(" ScriptingTypeHandle managedTypeHandle = object->GetTypeHandle();");
contents.AppendLine(" const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();"); contents.AppendLine(" const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();");
contents.AppendLine(" while (managedTypePtr->Script.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)"); contents.AppendLine(" while (managedTypePtr->Script.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)");
@@ -1317,97 +1320,175 @@ namespace Flax.Build.Bindings
contents.AppendLine(" managedTypeHandle = managedTypePtr->GetBaseType();"); contents.AppendLine(" managedTypeHandle = managedTypePtr->GetBaseType();");
contents.AppendLine(" managedTypePtr = &managedTypeHandle.GetType();"); contents.AppendLine(" managedTypePtr = &managedTypeHandle.GetType();");
contents.AppendLine(" }"); contents.AppendLine(" }");
contents.AppendLine(" if (WrapperCallInstance == object)"); contents.AppendLine(" if (WrapperCallInstance == object)");
contents.AppendLine(" {"); contents.AppendLine(" {");
GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "managedTypePtr->Script.ScriptVTableBase", scriptVTableOffset); GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "managedTypePtr->Script.ScriptVTableBase", scriptVTableOffset);
contents.AppendLine(" }"); contents.AppendLine(" }");
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;"); contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;");
contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);"); contents.AppendLine($" ASSERT(scriptVTable && scriptVTable[{scriptVTableOffset}]);");
contents.AppendLine($" auto method = scriptVTable[{scriptVTableOffset}];"); contents.AppendLine($" auto method = scriptVTable[{scriptVTableOffset}];");
contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");");
contents.AppendLine(" MonoObject* exception = nullptr;");
contents.AppendLine($" PROFILE_CPU_NAMED(\"{classInfo.FullNameManaged}::{functionInfo.Name}\");");
contents.AppendLine(" MonoObject* exception = nullptr;");
contents.AppendLine(" auto prevWrapperCallInstance = WrapperCallInstance;"); contents.AppendLine(" auto prevWrapperCallInstance = WrapperCallInstance;");
contents.AppendLine(" WrapperCallInstance = object;"); contents.AppendLine(" WrapperCallInstance = object;");
contents.AppendLine("#if USE_MONO_AOT");
if (functionInfo.Parameters.Count == 0) if (functionInfo.Parameters.Count == 0)
contents.AppendLine(" void** params = nullptr;"); contents.AppendLine(" void** params = nullptr;");
else else
contents.AppendLine($" void* params[{functionInfo.Parameters.Count}];"); contents.AppendLine($" void* params[{functionInfo.Parameters.Count}];");
for (var i = 0; i < functionInfo.Parameters.Count; i++) // If platform supports JITed code execution then use method thunk, otherwise fallback to generic mono_runtime_invoke
{
var parameterInfo = functionInfo.Parameters[i];
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, parameterInfo.IsOut, out _);
contents.Append($" params[{i}] = {paramValue};").AppendLine();
}
contents.AppendLine(" auto __result = mono_runtime_invoke(method->GetNative(), object->GetOrCreateManagedInstance(), params, &exception);");
contents.AppendLine("#else");
var thunkParams = string.Empty;
var thunkCall = string.Empty;
separator = functionInfo.Parameters.Count != 0;
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
if (separator)
thunkParams += ", ";
if (separator)
thunkCall += ", ";
separator = true;
thunkParams += "void*";
// Mono thunk call uses boxed values as objects
thunkCall += GenerateCppWrapperNativeToBox(buildData, parameterInfo.Type, classInfo, parameterInfo.Name);
}
var returnType = functionInfo.ReturnType; var returnType = functionInfo.ReturnType;
if (returnType.IsVoid) var useThunk = buildData.Platform.HasDynamicCodeExecutionSupport;
if (useThunk)
{ {
contents.AppendLine($" typedef void (*Thunk)(void* instance{thunkParams}, MonoObject** exception);"); // Convert parameters into managed format as boxed values
contents.AppendLine(" const auto thunk = (Thunk)method->GetThunk();"); var thunkParams = string.Empty;
contents.AppendLine($" thunk(object->GetOrCreateManagedInstance(){thunkCall}, &exception);"); var thunkCall = string.Empty;
if (functionInfo.Parameters.Count != 0)
{
separator = functionInfo.Parameters.Count != 0;
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
var paramValue = GenerateCppWrapperNativeToBox(buildData, parameterInfo.Type, classInfo, out var apiType, parameterInfo.Name);
var useLocalVar = false;
if (paramIsRef)
{
// Pass as pointer to value when using ref/out parameter
contents.Append($" auto __param_{parameterInfo.Name} = {paramValue};").AppendLine();
var useLocalVarPointer = !apiType.IsValueType;
paramValue = $"{(useLocalVarPointer ? "&" : "")}__param_{parameterInfo.Name}";
CppParamsThatNeedConversion[i] = useLocalVarPointer;
useLocalVar = true;
}
CppParamsThatNeedLocalVariable[i] = useLocalVar;
if (separator)
thunkParams += ", ";
if (separator)
thunkCall += ", ";
separator = true;
thunkParams += "void*";
contents.Append($" params[{i}] = {paramValue};").AppendLine();
thunkCall += $"params[{i}]";
}
}
// Invoke method thunk
if (returnType.IsVoid)
{
contents.AppendLine($" typedef void (*Thunk)(void* instance{thunkParams}, MonoObject** exception);");
contents.AppendLine(" const auto thunk = (Thunk)method->GetThunk();");
contents.AppendLine($" thunk(object->GetOrCreateManagedInstance(){thunkCall}, &exception);");
}
else
{
contents.AppendLine($" typedef MonoObject* (*Thunk)(void* instance{thunkParams}, MonoObject** exception);");
contents.AppendLine(" const auto thunk = (Thunk)method->GetThunk();");
contents.AppendLine($" auto __result = thunk(object->GetOrCreateManagedInstance(){thunkCall}, &exception);");
}
// Convert parameter values back from managed to native (could be modified there)
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
if (paramIsRef && !parameterInfo.Type.IsConst)
{
// Unbox from MonoObject*
parameterInfo.Type.IsRef = false;
var useLocalVarPointer = CppParamsThatNeedConversion[i];
var boxedValueCast = useLocalVarPointer ? "*(MonoObject**)" : "(MonoObject*)";
contents.Append($" {parameterInfo.Name} = MUtils::Unbox<{parameterInfo.Type}>({boxedValueCast}params[{i}]);").AppendLine();
parameterInfo.Type.IsRef = true;
}
}
} }
else else
{ {
contents.AppendLine($" typedef MonoObject* (*Thunk)(void* instance{thunkParams}, MonoObject** exception);"); // Convert parameters into managed format as pointers to value
contents.AppendLine(" const auto thunk = (Thunk)method->GetThunk();"); if (functionInfo.Parameters.Count != 0)
contents.AppendLine($" auto __result = thunk(object->GetOrCreateManagedInstance(){thunkCall}, &exception);"); {
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, paramIsRef, out CppParamsThatNeedLocalVariable[i]);
contents.Append($" params[{i}] = {paramValue};").AppendLine();
}
}
// Invoke method
contents.AppendLine(" auto __result = mono_runtime_invoke(method->GetNative(), object->GetOrCreateManagedInstance(), params, &exception);");
// Convert parameter values back from managed to native (could be modified there)
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
var paramIsRef = parameterInfo.IsRef || parameterInfo.IsOut;
if (paramIsRef && !parameterInfo.Type.IsConst)
{
// Direct value convert
var managedToNative = GenerateCppWrapperManagedToNative(buildData, parameterInfo.Type, classInfo, out var managedType, out var apiType, null, out _);
var passAsParamPtr = managedType.EndsWith("*");
var useLocalVarPointer = CppParamsThatNeedConversion[i] && !apiType.IsValueType;
var paramValue = useLocalVarPointer ? $"*({managedType}{(passAsParamPtr ? "" : "*")}*)params[{i}]" : $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]";
if (!string.IsNullOrEmpty(managedToNative))
{
if (!passAsParamPtr)
paramValue = '*' + paramValue;
paramValue = string.Format(managedToNative, paramValue);
}
else if (!passAsParamPtr)
paramValue = '*' + paramValue;
contents.Append($" {parameterInfo.Name} = {paramValue};").AppendLine();
}
}
} }
contents.AppendLine("#endif");
contents.AppendLine(" WrapperCallInstance = prevWrapperCallInstance;"); contents.AppendLine(" WrapperCallInstance = prevWrapperCallInstance;");
contents.AppendLine(" if (exception)"); contents.AppendLine(" if (exception)");
contents.AppendLine(" DebugLog::LogException(exception);"); contents.AppendLine(" DebugLog::LogException(exception);");
// Unpack returned value
if (!returnType.IsVoid) if (!returnType.IsVoid)
{ {
if (returnType.IsRef) if (returnType.IsRef)
throw new NotSupportedException($"Passing return value by reference is not supported for virtual API methods. Used on method '{functionInfo}'."); throw new NotSupportedException($"Passing return value by reference is not supported for virtual API methods. Used on method '{functionInfo}'.");
if (useThunk)
switch (returnType.Type)
{ {
case "bool": // Thunk might return value within pointer (eg. as int or boolean)
contents.AppendLine(" return __result != 0;"); switch (returnType.Type)
break; {
case "int8": case "bool":
case "int16": contents.AppendLine(" return __result != 0;");
case "int32": break;
case "int64": case "int8":
contents.AppendLine($" return ({returnType.Type})(intptr)__result;"); case "int16":
break; case "int32":
case "uint8": case "int64":
case "uint16": contents.AppendLine($" return ({returnType.Type})(intptr)__result;");
case "uint32": break;
case "uint64": case "uint8":
contents.AppendLine($" return ({returnType.Type})(uintptr)__result;"); case "uint16":
break; case "uint32":
default: case "uint64":
contents.AppendLine($" return ({returnType.Type})(uintptr)__result;");
break;
default:
contents.AppendLine($" return MUtils::Unbox<{returnType}>(__result);");
break;
}
}
else
{
// mono_runtime_invoke always returns boxed value as MonoObject*
contents.AppendLine($" return MUtils::Unbox<{returnType}>(__result);"); contents.AppendLine($" return MUtils::Unbox<{returnType}>(__result);");
break;
} }
} }
@@ -1732,8 +1813,8 @@ namespace Flax.Build.Bindings
{ {
var paramType = eventInfo.Type.GenericArgs[i]; var paramType = eventInfo.Type.GenericArgs[i];
var paramName = "arg" + i; var paramName = "arg" + i;
var paramIsOut = paramType.IsRef && !paramType.IsConst; var paramIsRef = paramType.IsRef && !paramType.IsConst;
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsOut, out CppParamsThatNeedConversion[i]); var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsRef, out CppParamsThatNeedConversion[i]);
contents.Append($" params[{i}] = {paramValue};").AppendLine(); contents.Append($" params[{i}] = {paramValue};").AppendLine();
} }
if (eventInfo.IsStatic) if (eventInfo.IsStatic)
@@ -1746,8 +1827,8 @@ namespace Flax.Build.Bindings
for (var i = 0; i < paramsCount; i++) for (var i = 0; i < paramsCount; i++)
{ {
var paramType = eventInfo.Type.GenericArgs[i]; var paramType = eventInfo.Type.GenericArgs[i];
var paramIsOut = paramType.IsRef && !paramType.IsConst; var paramIsRef = paramType.IsRef && !paramType.IsConst;
if (paramIsOut) if (paramIsRef)
{ {
// Convert value back from managed to native (could be modified there) // Convert value back from managed to native (could be modified there)
paramType.IsRef = false; paramType.IsRef = false;

View File

@@ -130,6 +130,11 @@ namespace Flax.Build
/// </summary> /// </summary>
public virtual bool HasExecutableFileReferenceSupport => false; public virtual bool HasExecutableFileReferenceSupport => false;
/// <summary>
/// Gets a value indicating whether that platform supports executing native code generated dynamically (JIT), otherwise requires ahead-of-time compilation (AOT).
/// </summary>
public virtual bool HasDynamicCodeExecutionSupport => true;
/// <summary> /// <summary>
/// Gets the executable file extension (including leading dot). /// Gets the executable file extension (including leading dot).
/// </summary> /// </summary>

View File

@@ -15,6 +15,9 @@ namespace Flax.Build.Platforms
/// <inheritdoc /> /// <inheritdoc />
public override TargetPlatform Target => TargetPlatform.UWP; public override TargetPlatform Target => TargetPlatform.UWP;
/// <inheritdoc />
public override bool HasDynamicCodeExecutionSupport => false;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="UWPPlatform"/> class. /// Initializes a new instance of the <see cref="UWPPlatform"/> class.
/// </summary> /// </summary>