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>
Array& operator=(std::initializer_list<T> initList) noexcept
{
Memory::DestructItems(_allocation.Get(), _count);
_count = _capacity = (int32)initList.size();
if (_capacity > 0)
Clear();
if (initList.size() > 0)
{
_allocation.Allocate(_capacity);
EnsureCapacity((int32)initList.size());
_count = (int32)initList.size();
Memory::ConstructItems(_allocation.Get(), initList.begin(), _count);
}
return *this;

View File

@@ -43,6 +43,15 @@ public:
{
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>
/// 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)
{
if (fx && fx->CanRender())
if (fx && fx->CanRender(renderContext))
postFx.Add(fx);
}
}
for (PostProcessEffect* fx : CustomPostFx)
{
if (fx && fx->CanRender())
if (fx && fx->CanRender(renderContext))
postFx.Add(fx);
}
if (const auto* camera = Camera.Get())
@@ -252,7 +252,7 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch,
for (Script* script : camera->Scripts)
{
auto* fx = Cast<PostProcessEffect>(script);
if (fx && fx->CanRender())
if (fx && fx->CanRender(renderContext))
postFx.Add(fx);
}
}

View File

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

View File

@@ -742,6 +742,23 @@ ScriptingTypeHandle Scripting::FindScriptingType(const StringAnsiView& fullname)
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)
{
if (type == nullptr)
@@ -772,10 +789,7 @@ ScriptingObject* Scripting::NewObject(const MClass* type)
const ScriptingObjectSpawnParams params(Guid::New(), ScriptingTypeHandle(module, typeIndex));
ScriptingObject* obj = scriptingType.Script.Spawn(params);
if (obj == nullptr)
{
LOG(Error, "Failed to spawn object of type \'{0}.{1}\'.", String(mono_class_get_namespace(typeClass)), String(mono_class_get_name(typeClass)));
return nullptr;
}
LOG(Error, "Failed to spawn object of type \'{0}\'.", scriptingType.ToString());
return obj;
#else
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>
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>
/// Creates a new instance of the given class object (native construction).
/// </summary>

View File

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

View File

@@ -63,10 +63,24 @@ namespace FlaxEngine
}
/// <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
return str.Length + base.TestMethod(str);
return str.Length + base.TestMethod(str, ref pod, struct1, ref struct2, out _);
}
/// <inheritdoc />

View File

@@ -17,6 +17,26 @@ API_STRUCT(NoDefault) struct TestStruct : public ISerializable
API_FIELD() Float3 Vector = Float3::One;
// Ref
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.
@@ -46,7 +66,7 @@ public:
API_EVENT() Delegate<int32, Float3, const String&, String&, const Array<TestStruct>&, Array<TestStruct>&> SimpleEvent;
// 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();
}

View File

@@ -274,6 +274,16 @@ namespace Flax.Build.Bindings
// Find across in-build types
if (InBuildTypes.TryGetValue(typeInfo, out 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
foreach (var e in buildData.ModulesInfo)

View File

@@ -99,7 +99,7 @@ namespace Flax.Build.Bindings
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;
var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null);
@@ -107,7 +107,7 @@ namespace Flax.Build.Bindings
if (!string.IsNullOrEmpty(nativeToManaged))
{
result = string.Format(nativeToManaged, paramName);
if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isOut)
if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*' && !isRef)
{
// Pass pointer value
}
@@ -124,7 +124,7 @@ namespace Flax.Build.Bindings
else
{
result = paramName;
if (paramType.IsRef && !paramType.IsConst && !isOut)
if (paramType.IsRef && !paramType.IsConst && !isRef)
{
// Pass reference as a pointer
result = '&' + result;
@@ -158,13 +158,13 @@ namespace Flax.Build.Bindings
{
var wrapperName = GenerateCppWrapperNativeToVariantMethodName(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)
{
var wrapperName = GenerateCppWrapperNativeToVariantMethodName(typeInfo.GenericArgs[0]);
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)
{
@@ -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
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
apiType = FindApiTypeInfo(buildData, typeInfo, caller);
if (apiType != null && apiType.IsScriptingObject)
return $"ScriptingObject::ToManaged((ScriptingObject*){value})";
@@ -1291,6 +1291,8 @@ namespace Flax.Build.Bindings
contents.Append(')');
contents.AppendLine();
contents.AppendLine(" {");
// Get object
string scriptVTableOffset;
if (classInfo.IsInterface)
{
@@ -1310,6 +1312,7 @@ namespace Flax.Build.Bindings
}
contents.AppendLine(" static THREADLOCAL void* WrapperCallInstance = nullptr;");
// Base method call
contents.AppendLine(" ScriptingTypeHandle managedTypeHandle = object->GetTypeHandle();");
contents.AppendLine(" const ScriptingType* managedTypePtr = &managedTypeHandle.GetType();");
contents.AppendLine(" while (managedTypePtr->Script.Spawn != &ManagedBinaryModule::ManagedObjectSpawn)");
@@ -1317,97 +1320,175 @@ namespace Flax.Build.Bindings
contents.AppendLine(" managedTypeHandle = managedTypePtr->GetBaseType();");
contents.AppendLine(" managedTypePtr = &managedTypeHandle.GetType();");
contents.AppendLine(" }");
contents.AppendLine(" if (WrapperCallInstance == object)");
contents.AppendLine(" {");
GenerateCppVirtualWrapperCallBaseMethod(buildData, contents, classInfo, functionInfo, "managedTypePtr->Script.ScriptVTableBase", scriptVTableOffset);
contents.AppendLine(" }");
contents.AppendLine(" auto scriptVTable = (MMethod**)managedTypePtr->Script.ScriptVTable;");
contents.AppendLine($" ASSERT(scriptVTable && 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(" WrapperCallInstance = object;");
contents.AppendLine("#if USE_MONO_AOT");
if (functionInfo.Parameters.Count == 0)
contents.AppendLine(" void** params = nullptr;");
else
contents.AppendLine($" void* params[{functionInfo.Parameters.Count}];");
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
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);
}
// If platform supports JITed code execution then use method thunk, otherwise fallback to generic mono_runtime_invoke
var returnType = functionInfo.ReturnType;
if (returnType.IsVoid)
var useThunk = buildData.Platform.HasDynamicCodeExecutionSupport;
if (useThunk)
{
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);");
// Convert parameters into managed format as boxed values
var thunkParams = string.Empty;
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
{
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 parameters into managed format as pointers to value
if (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 = 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(" if (exception)");
contents.AppendLine(" DebugLog::LogException(exception);");
// Unpack returned value
if (!returnType.IsVoid)
{
if (returnType.IsRef)
throw new NotSupportedException($"Passing return value by reference is not supported for virtual API methods. Used on method '{functionInfo}'.");
switch (returnType.Type)
if (useThunk)
{
case "bool":
contents.AppendLine(" return __result != 0;");
break;
case "int8":
case "int16":
case "int32":
case "int64":
contents.AppendLine($" return ({returnType.Type})(intptr)__result;");
break;
case "uint8":
case "uint16":
case "uint32":
case "uint64":
contents.AppendLine($" return ({returnType.Type})(uintptr)__result;");
break;
default:
// Thunk might return value within pointer (eg. as int or boolean)
switch (returnType.Type)
{
case "bool":
contents.AppendLine(" return __result != 0;");
break;
case "int8":
case "int16":
case "int32":
case "int64":
contents.AppendLine($" return ({returnType.Type})(intptr)__result;");
break;
case "uint8":
case "uint16":
case "uint32":
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);");
break;
}
}
@@ -1732,8 +1813,8 @@ namespace Flax.Build.Bindings
{
var paramType = eventInfo.Type.GenericArgs[i];
var paramName = "arg" + i;
var paramIsOut = paramType.IsRef && !paramType.IsConst;
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsOut, out CppParamsThatNeedConversion[i]);
var paramIsRef = paramType.IsRef && !paramType.IsConst;
var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, paramIsRef, out CppParamsThatNeedConversion[i]);
contents.Append($" params[{i}] = {paramValue};").AppendLine();
}
if (eventInfo.IsStatic)
@@ -1746,8 +1827,8 @@ namespace Flax.Build.Bindings
for (var i = 0; i < paramsCount; i++)
{
var paramType = eventInfo.Type.GenericArgs[i];
var paramIsOut = paramType.IsRef && !paramType.IsConst;
if (paramIsOut)
var paramIsRef = paramType.IsRef && !paramType.IsConst;
if (paramIsRef)
{
// Convert value back from managed to native (could be modified there)
paramType.IsRef = false;

View File

@@ -130,6 +130,11 @@ namespace Flax.Build
/// </summary>
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>
/// Gets the executable file extension (including leading dot).
/// </summary>

View File

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