Compare commits
25 Commits
0008435f1c
...
sdl_platfo
| Author | SHA1 | Date | |
|---|---|---|---|
| c7326ea483 | |||
| 523cad3b2c | |||
| ff6816396c | |||
| 24b8ad77fe | |||
| 3008d8037d | |||
| 0973363c64 | |||
| 5d45b9ea1c | |||
| c40f7c12f2 | |||
| 1b2d6372b2 | |||
| 0782ea889c | |||
| ef89501111 | |||
| 608353b996 | |||
| 221325ef09 | |||
| 8f57c91a9e | |||
| 968de34cae | |||
| 6586a98f8d | |||
| b3510b0e44 | |||
| d5a92c1942 | |||
| 417f82826f | |||
| 63bed0986a | |||
| c40eefc79d | |||
| d68631dd20 | |||
| e77b772010 | |||
| e41e956386 | |||
| 33e47c646b |
@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
|
||||
|
||||
:: Build bindings for all editor configurations
|
||||
echo Building C# bindings...
|
||||
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor
|
||||
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
|
||||
|
||||
popd
|
||||
echo Done!
|
||||
|
||||
@@ -334,7 +334,7 @@ Window* ManagedEditor::GetMainWindow()
|
||||
ASSERT(HasManagedInstance());
|
||||
const auto method = GetClass()->GetMethod("GetMainWindowPtr");
|
||||
ASSERT(method);
|
||||
return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr));
|
||||
return (Window*)MUtils::Unbox<void*>(method->Invoke(GetManagedInstance(), nullptr, nullptr), true);
|
||||
}
|
||||
|
||||
bool ManagedEditor::CanReloadScripts()
|
||||
@@ -346,7 +346,7 @@ bool ManagedEditor::CanReloadScripts()
|
||||
Internal_CanReloadScripts = GetClass()->GetMethod("Internal_CanReloadScripts");
|
||||
ASSERT(Internal_CanReloadScripts);
|
||||
}
|
||||
return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr));
|
||||
return MUtils::Unbox<bool>(Internal_CanReloadScripts->Invoke(GetManagedInstance(), nullptr, nullptr), true);
|
||||
}
|
||||
|
||||
bool ManagedEditor::CanAutoBuildCSG()
|
||||
@@ -365,7 +365,7 @@ bool ManagedEditor::CanAutoBuildCSG()
|
||||
Internal_CanAutoBuildCSG = GetClass()->GetMethod("Internal_CanAutoBuildCSG");
|
||||
ASSERT(Internal_CanAutoBuildCSG);
|
||||
}
|
||||
return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr));
|
||||
return MUtils::Unbox<bool>(Internal_CanAutoBuildCSG->Invoke(GetManagedInstance(), nullptr, nullptr), true);
|
||||
}
|
||||
|
||||
bool ManagedEditor::CanAutoBuildNavMesh()
|
||||
@@ -384,7 +384,7 @@ bool ManagedEditor::CanAutoBuildNavMesh()
|
||||
Internal_CanAutoBuildNavMesh = GetClass()->GetMethod("Internal_CanAutoBuildNavMesh");
|
||||
ASSERT(Internal_CanAutoBuildNavMesh);
|
||||
}
|
||||
return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr));
|
||||
return MUtils::Unbox<bool>(Internal_CanAutoBuildNavMesh->Invoke(GetManagedInstance(), nullptr, nullptr), true);
|
||||
}
|
||||
|
||||
bool ManagedEditor::HasGameViewportFocus() const
|
||||
@@ -397,7 +397,8 @@ bool ManagedEditor::HasGameViewportFocus() const
|
||||
Internal_HasGameViewportFocus = GetClass()->GetMethod("Internal_HasGameViewportFocus");
|
||||
ASSERT(Internal_HasGameViewportFocus);
|
||||
}
|
||||
result = MUtils::Unbox<bool>(Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr));
|
||||
auto invk = Internal_HasGameViewportFocus->Invoke(GetManagedInstance(), nullptr, nullptr);
|
||||
result = MUtils::Unbox<bool>(invk, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -495,7 +496,7 @@ bool ManagedEditor::OnAppExit()
|
||||
Internal_OnAppExit = GetClass()->GetMethod("Internal_OnAppExit");
|
||||
ASSERT(Internal_OnAppExit);
|
||||
}
|
||||
return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr));
|
||||
return MUtils::Unbox<bool>(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr), true);
|
||||
}
|
||||
|
||||
void ManagedEditor::RequestStartPlayOnEditMode()
|
||||
|
||||
@@ -334,6 +334,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
}
|
||||
}
|
||||
}
|
||||
if (editor == null)
|
||||
editor = Editor.Instance.CodeEditing.Editors[0];
|
||||
|
||||
Editor.Instance.CodeEditing.SelectedEditor = editor;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
var vsCode = codeEditing.GetInBuildEditor(CodeEditorTypes.VSCode);
|
||||
var rider = codeEditing.GetInBuildEditor(CodeEditorTypes.Rider);
|
||||
|
||||
#if PLATFORM_WINDOW
|
||||
#if PLATFORM_WINDOWS
|
||||
// Favor the newest Visual Studio
|
||||
for (int i = (int)CodeEditorTypes.VS2019; i >= (int)CodeEditorTypes.VS2008; i--)
|
||||
for (int i = (int)CodeEditorTypes.VS2026; i >= (int)CodeEditorTypes.VS2008; i--)
|
||||
{
|
||||
var visualStudio = codeEditing.GetInBuildEditor((CodeEditorTypes)i);
|
||||
if (visualStudio != null)
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
public string Name => "Default";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GenerateProjectCustomArgs => null;
|
||||
public string GenerateProjectCustomArgs => _currentEditor?.GenerateProjectCustomArgs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenSolution()
|
||||
|
||||
@@ -22,71 +22,14 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
public InBuildSourceCodeEditor(CodeEditorTypes type)
|
||||
{
|
||||
Type = type;
|
||||
switch (type)
|
||||
{
|
||||
case CodeEditorTypes.Custom:
|
||||
Name = "Custom";
|
||||
break;
|
||||
case CodeEditorTypes.SystemDefault:
|
||||
Name = "System Default";
|
||||
break;
|
||||
case CodeEditorTypes.VS2008:
|
||||
Name = "Visual Studio 2008";
|
||||
break;
|
||||
case CodeEditorTypes.VS2010:
|
||||
Name = "Visual Studio 2010";
|
||||
break;
|
||||
case CodeEditorTypes.VS2012:
|
||||
Name = "Visual Studio 2012";
|
||||
break;
|
||||
case CodeEditorTypes.VS2013:
|
||||
Name = "Visual Studio 2013";
|
||||
break;
|
||||
case CodeEditorTypes.VS2015:
|
||||
Name = "Visual Studio 2015";
|
||||
break;
|
||||
case CodeEditorTypes.VS2017:
|
||||
Name = "Visual Studio 2017";
|
||||
break;
|
||||
case CodeEditorTypes.VS2019:
|
||||
Name = "Visual Studio 2019";
|
||||
break;
|
||||
case CodeEditorTypes.VS2022:
|
||||
Name = "Visual Studio 2022";
|
||||
break;
|
||||
case CodeEditorTypes.VS2026:
|
||||
Name = "Visual Studio 2026";
|
||||
break;
|
||||
case CodeEditorTypes.VSCode:
|
||||
Name = "Visual Studio Code";
|
||||
break;
|
||||
case CodeEditorTypes.VSCodeInsiders:
|
||||
Name = "Visual Studio Code - Insiders";
|
||||
break;
|
||||
case CodeEditorTypes.Rider:
|
||||
Name = "Rider";
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
Name = CodeEditingManager.GetName(type);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GenerateProjectCustomArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case CodeEditorTypes.VSCodeInsiders:
|
||||
case CodeEditorTypes.VSCode: return "-vscode -vs2022";
|
||||
case CodeEditorTypes.Rider: return "-vs2022";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public string GenerateProjectCustomArgs => CodeEditingManager.GetGenerateProjectCustomArgs(Type);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenSolution()
|
||||
|
||||
@@ -139,6 +139,34 @@ CodeEditor* CodeEditingManager::GetCodeEditor(CodeEditorTypes editorType)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String CodeEditingManager::GetName(CodeEditorTypes editorType)
|
||||
{
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
if (editor)
|
||||
{
|
||||
return editor->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
|
||||
return String::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
String CodeEditingManager::GetGenerateProjectCustomArgs(CodeEditorTypes editorType)
|
||||
{
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
if (editor)
|
||||
{
|
||||
return editor->GetGenerateProjectCustomArgs();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Warning, "Missing code editor type {0}", (int32)editorType);
|
||||
return String::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeEditingManager::OpenFile(CodeEditorTypes editorType, const String& path, int32 line)
|
||||
{
|
||||
const auto editor = GetCodeEditor(editorType);
|
||||
|
||||
@@ -109,9 +109,18 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the name of the editor.
|
||||
/// </summary>
|
||||
/// <returns>The name</returns>
|
||||
/// <returns>The name.</returns>
|
||||
virtual String GetName() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
|
||||
/// </summary>
|
||||
/// <returns>The custom arguments to generate project files.</returns>
|
||||
virtual String GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return String::Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the file.
|
||||
/// </summary>
|
||||
@@ -169,6 +178,20 @@ public:
|
||||
/// <returns>The editor object or null if not found.</returns>
|
||||
static CodeEditor* GetCodeEditor(CodeEditorTypes editorType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the editor.
|
||||
/// </summary>
|
||||
/// <param name="editorType">The code editor type.</param>
|
||||
/// <returns>The name.</returns>
|
||||
API_FUNCTION() static String GetName(CodeEditorTypes editorType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor.
|
||||
/// </summary>
|
||||
/// <param name="editorType">The code editor type.</param>
|
||||
/// <returns>The custom arguments to generate project files.</returns>
|
||||
API_FUNCTION() static String GetGenerateProjectCustomArgs(CodeEditorTypes editorType);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the file. Handles async opening.
|
||||
/// </summary>
|
||||
|
||||
@@ -257,12 +257,17 @@ String RiderCodeEditor::GetName() const
|
||||
return TEXT("Rider");
|
||||
}
|
||||
|
||||
String RiderCodeEditor::GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return TEXT("-vs2022");
|
||||
}
|
||||
|
||||
void RiderCodeEditor::OpenFile(const String& path, int32 line)
|
||||
{
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject(TEXT("-vs2022"));
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open file
|
||||
@@ -290,7 +295,7 @@ void RiderCodeEditor::OpenSolution()
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject(TEXT("-vs2022"));
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open solution
|
||||
@@ -312,5 +317,5 @@ void RiderCodeEditor::OpenSolution()
|
||||
|
||||
void RiderCodeEditor::OnFileAdded(const String& path)
|
||||
{
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
// [CodeEditor]
|
||||
CodeEditorTypes GetType() const override;
|
||||
String GetName() const override;
|
||||
String GetGenerateProjectCustomArgs() const override;
|
||||
void OpenFile(const String& path, int32 line) override;
|
||||
void OpenSolution() override;
|
||||
void OnFileAdded(const String& path) override;
|
||||
|
||||
@@ -148,12 +148,17 @@ String VisualStudioEditor::GetName() const
|
||||
return String(ToString(_version));
|
||||
}
|
||||
|
||||
String VisualStudioEditor::GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return String::Format(TEXT("-{0}"), String(ToString(_version)).ToLower());
|
||||
}
|
||||
|
||||
void VisualStudioEditor::OpenFile(const String& path, int32 line)
|
||||
{
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open file
|
||||
@@ -172,7 +177,7 @@ void VisualStudioEditor::OpenSolution()
|
||||
// Generate project files if solution is missing
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
}
|
||||
|
||||
// Open solution
|
||||
@@ -187,7 +192,7 @@ void VisualStudioEditor::OpenSolution()
|
||||
void VisualStudioEditor::OnFileAdded(const String& path)
|
||||
{
|
||||
// TODO: finish dynamic files adding to the project - for now just regenerate it
|
||||
ScriptsBuilder::GenerateProject();
|
||||
ScriptsBuilder::GenerateProject(GetGenerateProjectCustomArgs());
|
||||
return;
|
||||
if (!FileSystem::FileExists(_solutionPath))
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
// [CodeEditor]
|
||||
CodeEditorTypes GetType() const override;
|
||||
String GetName() const override;
|
||||
String GetGenerateProjectCustomArgs() const override;
|
||||
void OpenFile(const String& path, int32 line) override;
|
||||
void OpenSolution() override;
|
||||
void OnFileAdded(const String& path) override;
|
||||
|
||||
@@ -128,6 +128,11 @@ String VisualStudioCodeEditor::GetName() const
|
||||
return _isInsiders ? TEXT("Visual Studio Code - Insiders") : TEXT("Visual Studio Code");
|
||||
}
|
||||
|
||||
String VisualStudioCodeEditor::GetGenerateProjectCustomArgs() const
|
||||
{
|
||||
return TEXT("-vs2022 -vscode");
|
||||
}
|
||||
|
||||
void VisualStudioCodeEditor::OpenFile(const String& path, int32 line)
|
||||
{
|
||||
// Generate VS solution files for intellisense
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
// [CodeEditor]
|
||||
CodeEditorTypes GetType() const override;
|
||||
String GetName() const override;
|
||||
String GetGenerateProjectCustomArgs() const override;
|
||||
void OpenFile(const String& path, int32 line) override;
|
||||
void OpenSolution() override;
|
||||
bool UseAsyncForOpen() const override;
|
||||
|
||||
@@ -12,6 +12,7 @@ using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Tabs;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
@@ -972,6 +973,9 @@ namespace FlaxEditor.Windows
|
||||
_cloneProjectButton.Clicked -= OnCloneProjectButtonClicked;
|
||||
PluginManager.PluginsChanged -= OnPluginsChanged;
|
||||
|
||||
Assert.IsTrue(!_entries.Any());
|
||||
_entries.Clear();
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
|
||||
if (set)
|
||||
mField->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mField->GetType(), failed));
|
||||
else
|
||||
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject));
|
||||
value = MUtils::UnboxVariant(mField->GetValueBoxed(instanceObject), true);
|
||||
return !failed;
|
||||
}
|
||||
else if (const auto mProperty = mClass->GetProperty(member.Get()))
|
||||
@@ -79,7 +79,7 @@ bool AccessVariant(Variant& instance, const StringAnsiView& member, Variant& val
|
||||
if (set)
|
||||
mProperty->SetValue(instanceObject, MUtils::VariantToManagedArgPtr(value, mProperty->GetType(), failed), nullptr);
|
||||
else
|
||||
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr));
|
||||
value = MUtils::UnboxVariant(mProperty->GetValue(instanceObject, nullptr), true);
|
||||
return !failed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
|
||||
}
|
||||
|
||||
// Extract result
|
||||
value = MUtils::UnboxVariant(result);
|
||||
value = MUtils::UnboxVariant(result, true);
|
||||
context.ValueCache.Add(boxBase, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -962,7 +962,7 @@ void SceneAnimationPlayer::Tick(SceneAnimation* anim, float time, float dt, int3
|
||||
else if (!MCore::Type::IsPointer(valueType) && !MCore::Type::IsReference(valueType))
|
||||
{
|
||||
if (boxed)
|
||||
Platform::MemoryCopy(value, MCore::Object::Unbox(boxed), valueSize);
|
||||
MCore::Object::Unbox(boxed, value);
|
||||
else
|
||||
Platform::MemoryClear(value, valueSize);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -19,7 +20,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
|
||||
_Pragma("clang diagnostic push") \
|
||||
@@ -37,8 +38,9 @@
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -52,7 +54,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
@@ -67,6 +69,7 @@
|
||||
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#define DLLIMPORT __declspec(dllimport)
|
||||
#define USED
|
||||
#define THREADLOCAL __declspec(thread)
|
||||
#define STDCALL __stdcall
|
||||
#define CDECL __cdecl
|
||||
|
||||
@@ -889,6 +889,11 @@ Variant::Variant(const Span<byte>& v)
|
||||
}
|
||||
}
|
||||
|
||||
Variant::Variant(const BytesContainer& v)
|
||||
: Variant((const Span<byte>&)(v))
|
||||
{
|
||||
}
|
||||
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
#include "Engine/Content/Deprecated.h"
|
||||
Variant::Variant(const CommonValue& value)
|
||||
@@ -4301,8 +4306,12 @@ void Variant::CopyStructure(void* src)
|
||||
if (MANAGED_GC_HANDLE && mclass->IsValueType())
|
||||
{
|
||||
MObject* instance = MCore::GCHandle::GetTarget(MANAGED_GC_HANDLE);
|
||||
void* data = MCore::Object::Unbox(instance);
|
||||
Platform::MemoryCopy(data, src, mclass->GetInstanceSize());
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::GCHandle::Free(MANAGED_GC_HANDLE);
|
||||
instance = MCore::Object::Box(src, mclass);
|
||||
MANAGED_GC_HANDLE = *(MGCHandle*)&instance;
|
||||
//void* data = MCore::Object::Unbox(instance);
|
||||
//Platform::MemoryCopy(data, src, mclass->GetInstanceSize());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,9 @@ struct CommonValue;
|
||||
template<typename T>
|
||||
class AssetReference;
|
||||
struct ScriptingTypeHandle;
|
||||
template<typename T>
|
||||
class DataContainer;
|
||||
typedef DataContainer<byte> BytesContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object type that can be interpreted as more than one type.
|
||||
@@ -244,6 +247,7 @@ public:
|
||||
explicit Variant(Dictionary<Variant, Variant, HeapAllocation>&& v);
|
||||
explicit Variant(const Dictionary<Variant, Variant, HeapAllocation>& v);
|
||||
explicit Variant(const Span<byte>& v);
|
||||
explicit Variant(const BytesContainer& v);
|
||||
explicit Variant(const CommonValue& v);
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "CommandLine.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include <iostream>
|
||||
|
||||
CommandLine::OptionsData CommandLine::Options;
|
||||
@@ -170,3 +171,63 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommandLine::ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments)
|
||||
{
|
||||
int32 start = 0;
|
||||
int32 quotesStart = -1;
|
||||
int32 length = cmdLine.Length();
|
||||
for (int32 i = 0; i < length; i++)
|
||||
{
|
||||
if (cmdLine[i] == ' ' && quotesStart == -1)
|
||||
{
|
||||
int32 count = i - start;
|
||||
if (count > 0)
|
||||
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
|
||||
start = i + 1;
|
||||
}
|
||||
else if (cmdLine[i] == '\"')
|
||||
{
|
||||
if (quotesStart >= 0)
|
||||
{
|
||||
if (i + 1 < length && cmdLine[i + 1] != ' ')
|
||||
{
|
||||
// End quotes are in the middle of the current word,
|
||||
// continue until the end of the current word.
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 offset = 1;
|
||||
if (quotesStart == start && cmdLine[start] == '\"')
|
||||
{
|
||||
// Word starts and ends with quotes, only include the quoted content.
|
||||
quotesStart++;
|
||||
offset--;
|
||||
}
|
||||
else if (quotesStart != start)
|
||||
{
|
||||
// Start quotes in the middle of the word, include the whole word.
|
||||
quotesStart = start;
|
||||
}
|
||||
|
||||
int32 count = i - quotesStart + offset;
|
||||
if (count > 0)
|
||||
arguments.Add(StringAnsi(cmdLine.Substring(quotesStart, count)));
|
||||
start = i + 1;
|
||||
}
|
||||
quotesStart = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
quotesStart = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
const int32 count = length - start;
|
||||
if (count > 0)
|
||||
arguments.Add(StringAnsi(cmdLine.Substring(start, count)));
|
||||
if (quotesStart >= 0)
|
||||
return true; // Missing last closing quote
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
/// <summary>
|
||||
/// Command line options helper.
|
||||
@@ -219,4 +220,12 @@ public:
|
||||
/// <param name="cmdLine">The command line.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool Parse(const Char* cmdLine);
|
||||
|
||||
/// <summary>
|
||||
/// Parses the command line arguments string into string list of arguments.
|
||||
/// </summary>
|
||||
/// <param name="cmdLine">The command line.</param>
|
||||
/// <param name="arguments">The parsed arguments</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool ParseArguments(const StringView& cmdLine, Array<StringAnsi>& arguments);
|
||||
};
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static IntPtr MarshalReturnValueString(ref string returnValue)
|
||||
{
|
||||
return returnValue != null ? ManagedString.ToNativeWeak(returnValue) : IntPtr.Zero;
|
||||
return returnValue != null ? ManagedString.ToNative/*Weak*/(returnValue) : IntPtr.Zero;
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueManagedHandle(ref ManagedHandle returnValue)
|
||||
@@ -157,7 +157,7 @@ namespace FlaxEngine.Interop
|
||||
if (returnObject == null)
|
||||
return IntPtr.Zero;
|
||||
if (returnType == typeof(string))
|
||||
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject));
|
||||
return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
|
||||
if (returnType == typeof(ManagedHandle))
|
||||
return ManagedHandle.ToIntPtr((ManagedHandle)(object)returnObject);
|
||||
if (returnType == typeof(bool))
|
||||
@@ -168,7 +168,7 @@ namespace FlaxEngine.Interop
|
||||
return ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
|
||||
if (returnType.IsArray)
|
||||
return ManagedHandle.ToIntPtr(ManagedArrayToGCHandleWrappedArray(Unsafe.As<Array>(returnObject)), GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
internal static IntPtr MarshalReturnValueThunk<TRet>(ref TRet returnValue)
|
||||
@@ -181,7 +181,7 @@ namespace FlaxEngine.Interop
|
||||
if (returnObject == null)
|
||||
return IntPtr.Zero;
|
||||
if (returnType == typeof(string))
|
||||
return ManagedString.ToNativeWeak(Unsafe.As<string>(returnObject));
|
||||
return ManagedString.ToNative/*Weak*/(Unsafe.As<string>(returnObject));
|
||||
if (returnType == typeof(IntPtr))
|
||||
return (IntPtr)(object)returnObject;
|
||||
if (returnType == typeof(ManagedHandle))
|
||||
@@ -210,7 +210,7 @@ namespace FlaxEngine.Interop
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt32)(object)returnObject);
|
||||
if (returnType == typeof(System.UInt64))
|
||||
return (IntPtr)new UIntPtr((ulong)(System.UInt64)(object)returnObject);
|
||||
return ManagedHandle.ToIntPtr(returnObject, GCHandleType.Weak);
|
||||
return ManagedHandle.ToIntPtr(returnObject/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
#if !USE_AOT
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#define USE_CONCURRENT_DICT
|
||||
#define USE_GCHANDLE
|
||||
//#define TRACK_HANDLES
|
||||
|
||||
#if USE_NETCORE
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -8,6 +12,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using FlaxEngine.Assertions;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
#pragma warning disable 1591
|
||||
|
||||
@@ -346,6 +351,14 @@ namespace FlaxEngine.Interop
|
||||
return;
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
[System.Diagnostics.DebuggerStepThrough]
|
||||
public static void Free(ManagedHandle handle)
|
||||
{
|
||||
if (handle == EmptyStringHandle)
|
||||
return;
|
||||
handle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -356,18 +369,106 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public struct ManagedHandle
|
||||
{
|
||||
#if USE_GCHANDLE
|
||||
private GCHandle handle;
|
||||
|
||||
#if TRACK_HANDLES
|
||||
private static HashSet<IntPtr> _weakHandles = new HashSet<nint>();
|
||||
private static ConcurrentDictionary<IntPtr, string> _handles = new();
|
||||
private static ConcurrentDictionary<IntPtr, string> _handles2 = new();
|
||||
private static long _allocations = 0;
|
||||
private static long _deallocations = 0;
|
||||
|
||||
public static long Allocations => Interlocked.Read(ref _allocations);
|
||||
public static long Deallocations => Interlocked.Read(ref _deallocations);
|
||||
#else
|
||||
public static long Allocations => 0;
|
||||
public static long Deallocations => 0;
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ManagedHandle(IntPtr handle) => this.handle = GCHandle.FromIntPtr(handle);
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private ManagedHandle(object value, GCHandleType type) //=> handle = GCHandle.Alloc(value, type);
|
||||
{
|
||||
#if TRACK_HANDLES
|
||||
/*if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
|
||||
{
|
||||
type = GCHandleType.Normal;
|
||||
}*/
|
||||
#endif
|
||||
handle = GCHandle.Alloc(value, type);
|
||||
#if TRACK_HANDLES
|
||||
/*if (type == GCHandleType.Weak || type == GCHandleType.WeakTrackResurrection)
|
||||
_weakHandles.Add((IntPtr)handle);
|
||||
else if (_handles.Count < 14000)
|
||||
_handles.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
|
||||
else
|
||||
{
|
||||
if (_handles2.Count > 12)
|
||||
type = type;
|
||||
if (value?.GetType() == typeof(string))
|
||||
type = type;
|
||||
_handles2.TryAdd((IntPtr)handle, value?.GetType().FullName ?? "");
|
||||
}*/
|
||||
Interlocked.Increment(ref _allocations);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
private IntPtr handle;
|
||||
|
||||
private ManagedHandle(IntPtr handle) => this.handle = handle;
|
||||
|
||||
private ManagedHandle(object value, GCHandleType type) => handle = ManagedHandlePool.AllocateHandle(value, type);
|
||||
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ManagedHandle Alloc(object value) => new ManagedHandle(value, GCHandleType.Normal);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ManagedHandle Alloc(object value, GCHandleType type) => new ManagedHandle(value, type);
|
||||
|
||||
#if USE_GCHANDLE
|
||||
public void Free()// => handle.Free();
|
||||
{
|
||||
if ((IntPtr)handle == IntPtr.Zero)
|
||||
return;
|
||||
#if TRACK_HANDLES
|
||||
/*if (_weakHandles.Remove((IntPtr)handle))
|
||||
{
|
||||
if (!handle.IsAllocated)
|
||||
handle = handle;
|
||||
handle = handle;
|
||||
}
|
||||
else if (_handles.Remove((IntPtr)handle, out _))
|
||||
{
|
||||
handle = handle;
|
||||
}
|
||||
else if (_handles2.Remove((IntPtr)handle, out _))
|
||||
{
|
||||
handle = handle;
|
||||
}*/
|
||||
Interlocked.Increment(ref _deallocations);
|
||||
#endif
|
||||
handle.Free();
|
||||
}
|
||||
|
||||
public object Target
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => handle.Target;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set => handle.Target = value;
|
||||
}
|
||||
|
||||
public bool IsAllocated
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => handle.IsAllocated;
|
||||
}
|
||||
#else
|
||||
public void Free()
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
@@ -383,6 +484,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
public bool IsAllocated => handle != IntPtr.Zero;
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator ManagedHandle(IntPtr value) => FromIntPtr(value);
|
||||
@@ -393,6 +495,16 @@ namespace FlaxEngine.Interop
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator IntPtr(ManagedHandle value) => ToIntPtr(value);
|
||||
|
||||
#if USE_GCHANDLE
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IntPtr ToIntPtr(object value) => (IntPtr)Alloc(value, GCHandleType.Normal).handle;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IntPtr ToIntPtr(object value, GCHandleType type) => (IntPtr)Alloc(value, type).handle;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IntPtr ToIntPtr(ManagedHandle value) => (IntPtr)value.handle;
|
||||
#else
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IntPtr ToIntPtr(object value) => ManagedHandlePool.AllocateHandle(value, GCHandleType.Normal);
|
||||
|
||||
@@ -401,17 +513,69 @@ namespace FlaxEngine.Interop
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IntPtr ToIntPtr(ManagedHandle value) => value.handle;
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => handle.GetHashCode();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj) => obj is ManagedHandle other && handle == other.handle;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(ManagedHandle other) => handle == other.handle;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(ManagedHandle a, ManagedHandle b) => a.handle == b.handle;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(ManagedHandle a, ManagedHandle b) => a.handle != b.handle;
|
||||
|
||||
#if USE_GCHANDLE
|
||||
/// <summary>
|
||||
/// A pool of shared managed handles used with short-lived temporary GCHandle allocations during interop.
|
||||
/// </summary>
|
||||
internal static class ManagedHandlePool
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static List<(bool InUse, ManagedHandle Handle)> _pool;
|
||||
|
||||
internal static void TryCollectWeakHandles(bool force = false)
|
||||
{
|
||||
}
|
||||
|
||||
internal static ManagedHandle Rent()
|
||||
{
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
|
||||
{
|
||||
if (tuple.InUse)
|
||||
continue;
|
||||
|
||||
tuple.InUse = true;
|
||||
return tuple.Handle;
|
||||
}
|
||||
|
||||
var newTuple = (InUse: true, Array: new ManagedHandle());
|
||||
_pool.Add(newTuple);
|
||||
return newTuple.Array;
|
||||
}
|
||||
|
||||
internal static void Return(ManagedHandle handle)
|
||||
{
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
|
||||
{
|
||||
if (tuple.Handle != handle)
|
||||
continue;
|
||||
|
||||
tuple.InUse = false;
|
||||
handle.Target = null;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("Tried to return non-pooled ManagedHandle");
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
internal static class ManagedHandlePool
|
||||
{
|
||||
private const int WeakPoolCollectionSizeThreshold = 10000000;
|
||||
@@ -425,17 +589,31 @@ namespace FlaxEngine.Interop
|
||||
// Dictionaries for storing the valid handles.
|
||||
// Note: Using locks seems to be generally the fastest when adding or fetching from the dictionary.
|
||||
// Concurrent dictionaries could also be considered, but they perform much slower when adding to the dictionary.
|
||||
#if USE_CONCURRENT_DICT
|
||||
private static ConcurrentDictionary<IntPtr, object> persistentPool = new();
|
||||
private static ConcurrentDictionary<IntPtr, GCHandle> pinnedPool = new();
|
||||
#else
|
||||
private static Dictionary<IntPtr, object> persistentPool = new();
|
||||
private static Dictionary<IntPtr, GCHandle> pinnedPool = new();
|
||||
private static Lock persistentPoolLock = new();
|
||||
private static Lock pinnedPoolLock = new();
|
||||
#endif
|
||||
|
||||
// TODO: Performance of pinned handles are poor at the moment due to GCHandle wrapping.
|
||||
// TODO: .NET8: Experiment with pinned arrays for faster pinning: https://github.com/dotnet/runtime/pull/89293
|
||||
|
||||
// Manage double-buffered pool for weak handles in order to avoid collecting in-flight handles.
|
||||
// Periodically when the pools are being accessed and conditions are met, the other pool is cleared and swapped.
|
||||
private static Dictionary<IntPtr, object> weakPool = new();
|
||||
private static Dictionary<IntPtr, object> weakPoolOther = new();
|
||||
private static object weakPoolLock = new object();
|
||||
private static int weakPoolSize = 1;
|
||||
private static int weakPoolOtherSize = 1;
|
||||
#if USE_CONCURRENT_DICT
|
||||
private static ConcurrentDictionary<IntPtr, object> weakPool = new(-1, weakPoolSize);
|
||||
private static ConcurrentDictionary<IntPtr, object> weakPoolOther = new(-1, weakPoolOtherSize);
|
||||
#else
|
||||
private static Dictionary<IntPtr, object> weakPool = new(weakPoolSize);
|
||||
private static Dictionary<IntPtr, object> weakPoolOther = new(weakPoolOtherSize);
|
||||
#endif
|
||||
private static Lock weakPoolLock = new();
|
||||
private static ulong nextWeakPoolCollection;
|
||||
private static int nextWeakPoolGCCollection;
|
||||
private static long lastWeakPoolCollectionTime;
|
||||
@@ -466,6 +644,19 @@ namespace FlaxEngine.Interop
|
||||
|
||||
// Swap the pools and release the oldest pool for GC
|
||||
(weakPool, weakPoolOther) = (weakPoolOther, weakPool);
|
||||
(weakPoolSize, weakPoolOtherSize) = (weakPoolOtherSize, weakPoolSize);
|
||||
#if USE_CONCURRENT_DICT
|
||||
if (weakPool.Count > weakPoolSize)
|
||||
{
|
||||
var newMax = Math.Max(Math.Max(weakPoolSize, weakPoolOtherSize), weakPool.Count);
|
||||
//FlaxEditor.Editor.Log($"growth from {weakPoolSize} to {weakPool.Count}, max {newMax}");
|
||||
weakPoolSize = newMax;
|
||||
weakPool = new(-1, weakPoolSize);
|
||||
}
|
||||
//else if (weakPool.Count > 0)
|
||||
// weakPool.Clear();
|
||||
//else
|
||||
#endif
|
||||
weakPool.Clear();
|
||||
}
|
||||
|
||||
@@ -485,14 +676,43 @@ namespace FlaxEngine.Interop
|
||||
internal static IntPtr AllocateHandle(object value, GCHandleType type)
|
||||
{
|
||||
IntPtr handle = NewHandle(type);
|
||||
#if USE_CONCURRENT_DICT
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
persistentPool.Add(handle, value);
|
||||
//lock (persistentPoolLock)
|
||||
persistentPool.TryAdd(handle, value);
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
//lock (pinnedPoolLock)
|
||||
pinnedPool.TryAdd(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
//weakPool.TryAdd(handle, value);
|
||||
weakPool[handle] = value;
|
||||
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPoolLock)
|
||||
persistentPool.Add(handle, value);
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPoolLock)
|
||||
pinnedPool.Add(handle, GCHandle.Alloc(value, GCHandleType.Pinned));
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
@@ -501,25 +721,36 @@ namespace FlaxEngine.Interop
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
weakPool.Add(handle, value);
|
||||
//weakPool[handle] = value;
|
||||
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static object GetObject(IntPtr handle)
|
||||
{
|
||||
switch (GetHandleType(handle))
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (persistentPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (persistentPool.TryGetValue(handle, out object value))
|
||||
return value;
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (pinnedPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (pinnedPool.TryGetValue(handle, out GCHandle gcHandle))
|
||||
return gcHandle.Target;
|
||||
@@ -537,15 +768,98 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
|
||||
}
|
||||
|
||||
internal static void SetObject(IntPtr handle, object value)
|
||||
{
|
||||
switch (GetHandleType(handle))
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
#if USE_CONCURRENT_DICT
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
//lock (persistentPoolLock)
|
||||
{
|
||||
//ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
|
||||
if (persistentPool.TryGetValue(handle, out var oldValue))
|
||||
{
|
||||
if (persistentPool.TryUpdate(handle, value, oldValue))
|
||||
//if (!Unsafe.IsNullRef(ref obj))
|
||||
{
|
||||
//obj = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
//lock (pinnedPoolLock)
|
||||
{
|
||||
/*ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref gcHandle))
|
||||
{
|
||||
gcHandle.Target = value;
|
||||
return;
|
||||
}*/
|
||||
if (pinnedPool.TryGetValue(handle, out var gcHandle))
|
||||
{
|
||||
gcHandle.Target = value;
|
||||
//if (pinnedPool.TryUpdate(handle, value, oldValue))
|
||||
//if (!Unsafe.IsNullRef(ref obj))
|
||||
//{
|
||||
//obj = value;
|
||||
return;
|
||||
}
|
||||
//}
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
if (weakPool.TryGetValue(handle, out var oldValue))
|
||||
{
|
||||
if (weakPool.TryUpdate(handle, value, oldValue))
|
||||
//if (!Unsafe.IsNullRef(ref obj))
|
||||
{
|
||||
//obj = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (weakPoolOther.TryGetValue(handle, out oldValue))
|
||||
{
|
||||
if (weakPoolOther.TryUpdate(handle, value, oldValue))
|
||||
//if (!Unsafe.IsNullRef(ref obj))
|
||||
{
|
||||
//obj = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*{
|
||||
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref obj))
|
||||
{
|
||||
obj = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(weakPoolOther, handle);
|
||||
if (!Unsafe.IsNullRef(ref obj))
|
||||
{
|
||||
obj = value;
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPoolLock)
|
||||
{
|
||||
ref object obj = ref CollectionsMarshal.GetValueRefOrNullRef(persistentPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref obj))
|
||||
@@ -556,7 +870,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
lock (pinnedPoolLock)
|
||||
{
|
||||
ref GCHandle gcHandle = ref CollectionsMarshal.GetValueRefOrNullRef(pinnedPool, handle);
|
||||
if (!Unsafe.IsNullRef(ref gcHandle))
|
||||
@@ -590,22 +904,32 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
break;
|
||||
}
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
#endif
|
||||
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
|
||||
}
|
||||
|
||||
internal static void FreeHandle(IntPtr handle)
|
||||
{
|
||||
switch (GetHandleType(handle))
|
||||
GCHandleType type = GetHandleType(handle);
|
||||
switch (type)
|
||||
{
|
||||
case GCHandleType.Normal:
|
||||
lock (persistentPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (persistentPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (persistentPool.Remove(handle))
|
||||
if (persistentPool.Remove(handle, out _))
|
||||
{
|
||||
//if (value?.GetType().Name.Contains("RenderContext") ?? false)
|
||||
// value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GCHandleType.Pinned:
|
||||
lock (pinnedPool)
|
||||
#if !USE_CONCURRENT_DICT
|
||||
lock (pinnedPoolLock)
|
||||
#endif
|
||||
{
|
||||
if (pinnedPool.Remove(handle, out GCHandle gcHandle))
|
||||
{
|
||||
@@ -617,12 +941,19 @@ namespace FlaxEngine.Interop
|
||||
case GCHandleType.Weak:
|
||||
case GCHandleType.WeakTrackResurrection:
|
||||
lock (weakPoolLock)
|
||||
{
|
||||
TryCollectWeakHandles();
|
||||
return;
|
||||
if (weakPool.Remove(handle, out _))
|
||||
return;
|
||||
else if (weakPoolOther.Remove(handle, out _))
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new NativeInteropException("Invalid ManagedHandle");
|
||||
throw new NativeInteropException($"Invalid ManagedHandle of type '{type}'");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if USE_NETCORE
|
||||
using FlaxEngine.Assertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@@ -59,8 +60,9 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
public struct ManagedToNativeState
|
||||
{
|
||||
ManagedArray managedArray;
|
||||
IntPtr handle;
|
||||
ManagedArray _pooledManagedArray;
|
||||
ManagedArray _managedArray;
|
||||
ManagedHandle handle;
|
||||
|
||||
public void FromManaged(object managed)
|
||||
{
|
||||
@@ -73,29 +75,30 @@ namespace FlaxEngine.Interop
|
||||
if (NativeInterop.ArrayFactory.GetMarshalledType(elementType) == elementType)
|
||||
{
|
||||
// Use pooled managed array wrapper to be passed around as handle to it
|
||||
(ManagedHandle tmp, managedArray) = ManagedArray.WrapPooledArray(arr);
|
||||
handle = ManagedHandle.ToIntPtr(tmp);
|
||||
(handle, _pooledManagedArray) = ManagedArray.WrapPooledArray(arr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert array contents to be properly accessed by the native code (as GCHandles array)
|
||||
managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
|
||||
handle = ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managedArray));
|
||||
managedArray = null; // It's not pooled
|
||||
_managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(arr);
|
||||
handle = ManagedHandle.Alloc(_pooledManagedArray);
|
||||
}
|
||||
}
|
||||
else
|
||||
handle = ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
public IntPtr ToUnmanaged()
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
public IntPtr ToUnmanaged() => ManagedHandle.ToIntPtr(handle);
|
||||
|
||||
public void Free()
|
||||
{
|
||||
managedArray?.FreePooled();
|
||||
if (_pooledManagedArray != null)
|
||||
_pooledManagedArray.FreePooled();
|
||||
else
|
||||
{
|
||||
handle.Free();
|
||||
_managedArray?.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +112,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
ManagedHandle.FromIntPtr(unmanaged).Free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,27 +323,24 @@ namespace FlaxEngine.Interop
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public struct ManagedToNative
|
||||
public static class ManagedToNative
|
||||
{
|
||||
ManagedHandle handle;
|
||||
|
||||
public void FromManaged(object[] managed)
|
||||
public static object[] ConvertToManaged(NativeArray<IntPtr> unmanaged)
|
||||
{
|
||||
if (managed != null)
|
||||
{
|
||||
var managedArray = NativeInterop.ManagedArrayToGCHandleWrappedArray(managed);
|
||||
handle = ManagedHandle.Alloc(managedArray, GCHandleType.Weak);
|
||||
}
|
||||
if (unmanaged.Data == null)
|
||||
return null;
|
||||
return NativeInterop.GCHandleArrayToManagedArray<object>(unmanaged);
|
||||
}
|
||||
|
||||
public IntPtr ToUnmanaged()
|
||||
public static NativeArray<IntPtr> ConvertToUnmanaged(object[] managed)
|
||||
{
|
||||
return ManagedHandle.ToIntPtr(handle);
|
||||
if (managed == null)
|
||||
return new NativeArray<IntPtr>();
|
||||
return NativeInterop.ManagedArrayToGCHandleWrappedArray(managed);
|
||||
}
|
||||
|
||||
public void Free()
|
||||
public static void Free(NativeArray<IntPtr> unmanaged)
|
||||
{
|
||||
handle.Free();
|
||||
if (unmanaged.Data != null)
|
||||
unmanaged.Free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,7 +410,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
//DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
|
||||
DictionaryMarshaller<T, U>.Free(unmanaged); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,6 +659,431 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(BoxedArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(BoxedArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(BoxedArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(BoxedArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(BoxedArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(BoxedArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(BoxedArrayMarshaller<,>.Bidirectional))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(BoxedArrayMarshaller<,>.Bidirectional))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(BoxedArrayMarshaller<,>))]
|
||||
[ContiguousCollectionMarshaller]
|
||||
public static unsafe class BoxedArrayMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static NativeArray<TUnmanagedElement> AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||
{
|
||||
if (managed == null)
|
||||
{
|
||||
numElements = 0;
|
||||
return new NativeArray<TUnmanagedElement>();
|
||||
}
|
||||
numElements = managed.Length;
|
||||
return new NativeArray<TUnmanagedElement>(managed.Length);
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed)
|
||||
{
|
||||
if (managed == null)
|
||||
return null;
|
||||
return managed.AsSpan();
|
||||
}
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return null;
|
||||
return unmanaged.AsSpan();
|
||||
}
|
||||
|
||||
public static void Free(NativeArray<TUnmanagedElement> unmanaged)
|
||||
{
|
||||
if (unmanaged.Data != null)
|
||||
unmanaged.Free();
|
||||
}
|
||||
|
||||
public static T[] AllocateContainerForManagedElements(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Span<T> GetManagedValuesDestination(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static T[] AllocateContainerForManagedElements(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return null;
|
||||
Assert.IsTrue(unmanaged.Length == numElements);
|
||||
return new T[unmanaged.Length];
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return null;
|
||||
return unmanaged.AsReadOnlySpan();
|
||||
}
|
||||
|
||||
public static Span<T> GetManagedValuesDestination(T[] managed)
|
||||
{
|
||||
if (managed == null)
|
||||
return null;
|
||||
return managed.AsSpan();
|
||||
}
|
||||
|
||||
public static void Free(NativeArray<TUnmanagedElement> unmanaged)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return;
|
||||
Marshal.FreeCoTaskMem((IntPtr)unmanaged.Data);
|
||||
}
|
||||
|
||||
public static NativeArray<TUnmanagedElement> AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException();
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => throw new NotImplementedException();
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(NativeArray<TUnmanagedElement> unmanaged, int numElements) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public struct Bidirectional
|
||||
{
|
||||
//string managed;
|
||||
//IntPtr unmanaged;
|
||||
|
||||
public void FromManaged(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public T[] ToManaged()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<TUnmanagedElement> GetManagedValuesSource()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Span<TUnmanagedElement> GetUnmanagedValuesDestination()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Span<TUnmanagedElement> GetManagedValuesDestination(int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public NativeArray<TUnmanagedElement> ToUnmanaged()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void FromUnmanaged(NativeArray<TUnmanagedElement> unmanaged)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static NativeArray<TUnmanagedElement> AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static T[] AllocateContainerForManagedElements(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Span<T> GetManagedValuesDestination(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe /*ref*/ struct NativeArray<T> where T : unmanaged
|
||||
{
|
||||
public T* Data;
|
||||
public int Length;
|
||||
|
||||
public NativeArray(T* data, int length)
|
||||
{
|
||||
Data = data;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public NativeArray(Span<T> data)
|
||||
{
|
||||
Data = (T*)Marshal.AllocCoTaskMem(data.Length);
|
||||
Length = data.Length;
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
Unsafe.Copy(&Data[i], ref data[i]);
|
||||
}
|
||||
|
||||
public NativeArray(IntPtr data, int length)
|
||||
{
|
||||
Data = (T*)data;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public NativeArray(int length)
|
||||
{
|
||||
Data = (T*)Marshal.AllocCoTaskMem(length);
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public NativeArray()
|
||||
{
|
||||
Data = null;
|
||||
Length = 0;
|
||||
}
|
||||
|
||||
public void Free() => Marshal.FreeCoTaskMem((IntPtr)Data);
|
||||
|
||||
public Span<T> AsSpan() => new Span<T>(Data, Length);
|
||||
public ReadOnlySpan<T> AsReadOnlySpan() => new Span<T>(Data, Length);
|
||||
|
||||
public static bool operator ==(NativeArray<T> left, NativeArray<T> right) => left.Data == right.Data && left.Length == right.Length;
|
||||
public static bool operator !=(NativeArray<T> left, NativeArray<T> right) => left.Data != right.Data || left.Length != right.Length;
|
||||
public static bool operator ==(NativeArray<T> left, IntPtr right) => left.Data == (T*)right;
|
||||
public static bool operator !=(NativeArray<T> left, IntPtr right) => left.Data != (T*)right;
|
||||
public static bool operator ==(IntPtr left, NativeArray<T> right) => (T*)left == right.Data;
|
||||
public static bool operator !=(IntPtr left, NativeArray<T> right) => (T*)left != right.Data;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null) return false;
|
||||
if (obj is NativeArray<T> arr) return this == arr;
|
||||
if (obj is IntPtr ptr) return this == ptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => ((IntPtr)Data).GetHashCode() * 397 ^ Length.GetHashCode();
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedIn, typeof(NativeArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedOut, typeof(NativeArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementIn, typeof(NativeArrayMarshaller<,>.ManagedToNative))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedOut, typeof(NativeArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedIn, typeof(NativeArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementOut, typeof(NativeArrayMarshaller<,>.NativeToManaged))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ManagedToUnmanagedRef, typeof(NativeArrayMarshaller<,>.Bidirectional))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.UnmanagedToManagedRef, typeof(NativeArrayMarshaller<,>.Bidirectional))]
|
||||
[CustomMarshaller(typeof(CustomMarshallerAttribute.GenericPlaceholder[]), MarshalMode.ElementRef, typeof(NativeArrayMarshaller<,>))]
|
||||
[ContiguousCollectionMarshaller]
|
||||
public static unsafe class NativeArrayMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
|
||||
{
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static NativeArray<TUnmanagedElement> AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException();
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => throw new NotImplementedException();
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(NativeArray<TUnmanagedElement> unmanaged, int numElements) => throw new NotImplementedException();
|
||||
|
||||
public static void Free(NativeArray<TUnmanagedElement> unmanaged) => throw new NotImplementedException();
|
||||
|
||||
public static T[] AllocateContainerForManagedElements(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Span<T> GetManagedValuesDestination(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static T[] AllocateContainerForManagedElements(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return Array.Empty<T>();
|
||||
Assert.IsTrue(unmanaged.Length == numElements);
|
||||
return new T[unmanaged.Length];
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return null;
|
||||
return unmanaged.AsReadOnlySpan();
|
||||
}
|
||||
|
||||
public static Span<T> GetManagedValuesDestination(T[] managed)
|
||||
{
|
||||
if (managed == null)
|
||||
return null;
|
||||
return managed.AsSpan();
|
||||
}
|
||||
|
||||
public static void Free(NativeArray<TUnmanagedElement> unmanaged)
|
||||
{
|
||||
if (unmanaged.Data == null)
|
||||
return;
|
||||
Marshal.FreeCoTaskMem((IntPtr)unmanaged.Data);
|
||||
}
|
||||
|
||||
public static NativeArray<TUnmanagedElement> AllocateContainerForUnmanagedElements(T[] managed, out int numElements) => throw new NotImplementedException();
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed) => throw new NotImplementedException();
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(NativeArray<TUnmanagedElement> unmanaged, int numElements) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
public struct Bidirectional
|
||||
{
|
||||
//string managed;
|
||||
//IntPtr unmanaged;
|
||||
|
||||
public void FromManaged(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public T[] ToManaged()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<TUnmanagedElement> GetManagedValuesSource()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Span<TUnmanagedElement> GetUnmanagedValuesDestination()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Span<TUnmanagedElement> GetManagedValuesDestination(int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public NativeArray<TUnmanagedElement> ToUnmanaged()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void FromUnmanaged(NativeArray<TUnmanagedElement> unmanaged)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Free()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static NativeArray<TUnmanagedElement> AllocateContainerForUnmanagedElements(T[] managed, out int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static T[] AllocateContainerForManagedElements(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<T> GetManagedValuesSource(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(NativeArray<TUnmanagedElement> unmanaged, int numElements)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Span<T> GetManagedValuesDestination(T[] managed)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
#endif
|
||||
@@ -678,7 +1104,7 @@ namespace FlaxEngine.Interop
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
|
||||
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/);
|
||||
public static void Free(IntPtr unmanaged) => ManagedString.Free(unmanaged);
|
||||
}
|
||||
|
||||
@@ -688,11 +1114,11 @@ namespace FlaxEngine.Interop
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static string ConvertToManaged(IntPtr unmanaged) => ManagedString.ToManaged(unmanaged);
|
||||
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed, GCHandleType.Weak);
|
||||
public static unsafe IntPtr ConvertToUnmanaged(string managed) => managed == null ? IntPtr.Zero : ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/);
|
||||
|
||||
public static void Free(IntPtr unmanaged)
|
||||
{
|
||||
//ManagedString.Free(unmanaged); // No need to free weak handles
|
||||
ManagedString.Free(unmanaged); // No need to free weak handles
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using System.Threading;
|
||||
using FlaxEngine.Assertions;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
#pragma warning disable 1591
|
||||
@@ -543,6 +545,18 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FreeArray(ManagedHandle handle)
|
||||
{
|
||||
if (!handle.IsAllocated)
|
||||
return;
|
||||
ManagedArray managedArray = Unsafe.As<ManagedArray>(handle.Target);
|
||||
if (managedArray.ElementType.IsValueType)
|
||||
managedArray.Free();
|
||||
else
|
||||
managedArray.FreePooled();
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle GetArrayTypeFromElementType(ManagedHandle elementTypeHandle)
|
||||
{
|
||||
@@ -602,13 +616,19 @@ namespace FlaxEngine.Interop
|
||||
[UnmanagedCallersOnly]
|
||||
internal static IntPtr NewStringUTF16(char* text, int length)
|
||||
{
|
||||
return ManagedString.ToNativeWeak(new string(new ReadOnlySpan<char>(text, length)));
|
||||
return ManagedString.ToNative/*Weak*/(new string(new ReadOnlySpan<char>(text, length)));
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static IntPtr NewStringUTF8(sbyte* text, int length)
|
||||
{
|
||||
return ManagedString.ToNativeWeak(new string(text, 0, length, System.Text.Encoding.UTF8));
|
||||
return ManagedString.ToNative/*Weak*/(new string(text, 0, length, System.Text.Encoding.UTF8));
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
internal static void FreeString(ManagedHandle handle)
|
||||
{
|
||||
ManagedString.Free(handle);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -654,27 +674,30 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a managed copy of the value, and stores it in a boxed reference.
|
||||
/// Creates a managed converted copy of the unmanaged value, and boxes it in a managed handle.
|
||||
/// </summary>
|
||||
/// <param name="typeHandle">A handle to class type.</param>
|
||||
/// <param name="valuePtr">A pointer to unmanaged value.</param>
|
||||
[UnmanagedCallersOnly]
|
||||
internal static ManagedHandle BoxValue(ManagedHandle typeHandle, IntPtr valuePtr)
|
||||
{
|
||||
Type type = Unsafe.As<TypeHolder>(typeHandle.Target);
|
||||
object value = MarshalToManaged(valuePtr, type);
|
||||
return ManagedHandle.Alloc(value, GCHandleType.Weak);
|
||||
return ManagedHandle.Alloc(value/*, GCHandleType.Weak*/);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the address of the boxed value type.
|
||||
/// Writes the unboxed converted value to given address.
|
||||
/// </summary>
|
||||
/// <param name="objectHandle">A handle to boxed object.</param>
|
||||
/// <param name="destinationPtr">The destination address for unboxed value.</param>
|
||||
[UnmanagedCallersOnly]
|
||||
internal static IntPtr UnboxValue(ManagedHandle handle)
|
||||
internal static void UnboxValue(ManagedHandle objectHandle, IntPtr destinationPtr)
|
||||
{
|
||||
object value = handle.Target;
|
||||
object value = objectHandle.Target;
|
||||
Assert.IsNotNull(value, "Boxed value type can't be null.");
|
||||
Type type = value.GetType();
|
||||
if (!type.IsValueType)
|
||||
return ManagedHandle.ToIntPtr(handle);
|
||||
return ValueTypeUnboxer.GetPointer(value, type);
|
||||
MarshalToNative(objectHandle.Target, destinationPtr, type);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
@@ -714,7 +737,7 @@ namespace FlaxEngine.Interop
|
||||
internal static IntPtr InvokeMethod(ManagedHandle instanceHandle, ManagedHandle methodHandle, IntPtr paramPtr, IntPtr exceptionPtr)
|
||||
{
|
||||
MethodHolder methodHolder = Unsafe.As<MethodHolder>(methodHandle.Target);
|
||||
#if !USE_AOT
|
||||
#if false//#if !USE_AOT
|
||||
if (methodHolder.TryGetDelegate(out var methodDelegate, out var methodDelegateContext))
|
||||
{
|
||||
// Fast path, invoke the method with minimal allocations
|
||||
@@ -737,7 +760,7 @@ namespace FlaxEngine.Interop
|
||||
// Slow path, method parameters needs to be stored in heap
|
||||
object returnObject;
|
||||
int numParams = methodHolder.parameterTypes.Length;
|
||||
object[] methodParameters = new object[numParams];
|
||||
object[] methodParameters = new object[numParams];//ObjectArrayPool.Rent(numParams);//new object[numParams];
|
||||
|
||||
for (int i = 0; i < numParams; i++)
|
||||
{
|
||||
@@ -748,6 +771,17 @@ namespace FlaxEngine.Interop
|
||||
try
|
||||
{
|
||||
returnObject = methodHolder.method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
|
||||
|
||||
// Marshal reference parameters back to original unmanaged references
|
||||
for (int i = 0; i < numParams; i++)
|
||||
{
|
||||
Type parameterType = methodHolder.parameterTypes[i];
|
||||
if (parameterType.IsByRef)
|
||||
{
|
||||
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
|
||||
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -762,19 +796,11 @@ namespace FlaxEngine.Interop
|
||||
throw realException;
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// Marshal reference parameters back to original unmanaged references
|
||||
for (int i = 0; i < numParams; i++)
|
||||
finally
|
||||
{
|
||||
Type parameterType = methodHolder.parameterTypes[i];
|
||||
if (parameterType.IsByRef)
|
||||
{
|
||||
IntPtr nativePtr = Unsafe.Read<IntPtr>((IntPtr.Add(paramPtr, sizeof(IntPtr) * i)).ToPointer());
|
||||
MarshalToNative(methodParameters[i], nativePtr, parameterType.GetElementType());
|
||||
}
|
||||
//ObjectArrayPool.Return(methodParameters);
|
||||
}
|
||||
|
||||
// Return value
|
||||
return Invoker.MarshalReturnValueGeneric(methodHolder.returnType, returnObject);
|
||||
}
|
||||
}
|
||||
@@ -908,7 +934,7 @@ namespace FlaxEngine.Interop
|
||||
if (File.Exists(pdbPath))
|
||||
{
|
||||
// Load including debug symbols
|
||||
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open);
|
||||
using FileStream pdbStream = new FileStream(Path.ChangeExtension(assemblyPath, "pdb"), FileMode.Open, FileAccess.Read);
|
||||
assembly = scriptingAssemblyLoadContext.LoadFromStream(stream, pdbStream);
|
||||
}
|
||||
else
|
||||
@@ -998,6 +1024,8 @@ namespace FlaxEngine.Interop
|
||||
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Scripting AssemblyLoadContext was not unloaded.");
|
||||
weakRef.Free();
|
||||
|
||||
Assert.IsFalse(AssemblyLoadContext.All.Any(x => x.Name == "Flax"));
|
||||
|
||||
static bool IsHandleAlive(GCHandle weakRef)
|
||||
{
|
||||
// Checking the target in scope somehow holds a reference to it...?
|
||||
@@ -1026,6 +1054,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
// Clear all caches which might hold references to assemblies in collectible ALC
|
||||
cachedDelegatesCollectible.Clear();
|
||||
cachedDelegatesCollectible = new();
|
||||
foreach (var pair in managedTypesCollectible)
|
||||
pair.Value.handle.Free();
|
||||
managedTypesCollectible.Clear();
|
||||
@@ -1115,6 +1144,9 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
// Unload the ALC
|
||||
scriptingAssemblyLoadContext.Unload();
|
||||
scriptingAssemblyLoadContext.Resolving -= OnScriptingAssemblyLoadContextResolving;
|
||||
|
||||
@@ -18,6 +18,7 @@ using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Buffers;
|
||||
|
||||
namespace FlaxEngine.Interop
|
||||
{
|
||||
@@ -187,6 +188,28 @@ namespace FlaxEngine.Interop
|
||||
return Marshal.GetFunctionPointerForDelegate<TDelegate>(d);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts array of GC Handles from native runtime to managed array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Array element type.</typeparam>
|
||||
/// <param name="ptrArray">Input array.</param>
|
||||
/// <param name="buffer">Cached memory allocation buffer to use for the result (if size fits).</param>
|
||||
/// <returns>Output array.</returns>
|
||||
public static T[] GCHandleArrayToManagedArray<T>(NativeArray<IntPtr> ptrArray, T[] buffer = null) where T : class
|
||||
{
|
||||
ReadOnlySpan<IntPtr> span = ptrArray.AsReadOnlySpan();
|
||||
if (buffer != null)
|
||||
buffer = buffer;
|
||||
if (buffer == null || buffer.Length < ptrArray.Length)
|
||||
buffer = new T[ptrArray.Length];
|
||||
for (int i = 0; i < ptrArray.Length; i++)
|
||||
{
|
||||
IntPtr ptr = span[i];
|
||||
buffer[i] = ptr != IntPtr.Zero ? (T)ManagedHandle.FromIntPtr(ptr).Target : default;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts array of GC Handles from native runtime to managed array.
|
||||
/// </summary>
|
||||
@@ -237,6 +260,24 @@ namespace FlaxEngine.Interop
|
||||
return ManagedArray.WrapNewArray(pointerArray, array.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts managed array wrapper into array of GC Handles for native runtime.
|
||||
/// </summary>
|
||||
/// <param name="array">Input array.</param>
|
||||
/// <returns>Output array.</returns>
|
||||
public static NativeArray<IntPtr> ManagedArrayToGCHandleWrappedArray<T>(T[] array)
|
||||
{
|
||||
NativeArray<IntPtr> pointerArray = new(array.Length);
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
//foreach (var ptr in pointerArray.AsSpan())
|
||||
{
|
||||
pointerArray.Data[i] = array[i] != null ? ManagedHandle.ToIntPtr(array[i]) : IntPtr.Zero;
|
||||
}
|
||||
return pointerArray;
|
||||
//IntPtr[] pointerArray = ManagedArrayToGCHandleArray(array);
|
||||
//return ManagedArray.WrapNewArray(pointerArray, array.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts array with a custom converter function for each element.
|
||||
/// </summary>
|
||||
@@ -269,6 +310,23 @@ namespace FlaxEngine.Interop
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts array with a custom converter function for each element.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSrc">Input data type.</typeparam>
|
||||
/// <typeparam name="TDst">Output data type.</typeparam>
|
||||
/// <param name="src">The input array.</param>
|
||||
/// <param name="convertFunc">Converter callback.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
public static NativeArray<TDst> ConvertArrayNative<TSrc, TDst>(this Span<TSrc> src, Func<TSrc, TDst> convertFunc)
|
||||
where TDst : unmanaged
|
||||
{
|
||||
NativeArray<TDst> dst = new(src.Length);
|
||||
for (int i = 0; i < src.Length; i++)
|
||||
dst.Data[i] = convertFunc(src[i]);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/// <summary>Find <paramref name="assemblyName"/> among the scripting assemblies.</summary>
|
||||
/// <param name="assemblyName">The name to find</param>
|
||||
/// <param name="allowPartial">If true, partial names should be allowed to be resolved.</param>
|
||||
@@ -1139,13 +1197,18 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static void ToManagedArray(ref T[] managedValue, IntPtr nativePtr, bool byRef)
|
||||
{
|
||||
if (byRef)
|
||||
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
|
||||
//if (byRef)
|
||||
// nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
|
||||
|
||||
if (nativePtr != IntPtr.Zero)
|
||||
{
|
||||
ManagedArray managedArray = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(nativePtr).Target);
|
||||
managedValue = Unsafe.As<T[]>(managedArray.ToArray<T>());
|
||||
int length = Unsafe.Read<int>(IntPtr.Add(nativePtr, Unsafe.SizeOf<IntPtr>()).ToPointer());
|
||||
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
|
||||
|
||||
var span = new Span<T>(nativePtr.ToPointer(), length);
|
||||
managedValue = span.ToArray();
|
||||
//ManagedArray managedArray = Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(nativePtr).Target);
|
||||
//managedValue = Unsafe.As<T[]>(managedArray.ToArray<T>());
|
||||
}
|
||||
else
|
||||
managedValue = null;
|
||||
@@ -1244,7 +1307,7 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static void ToNativeString(ref string managedValue, IntPtr nativePtr)
|
||||
{
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNativeWeak(managedValue));
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedString.ToNative/*Weak*/(managedValue));
|
||||
}
|
||||
|
||||
internal static void ToNativeType(ref Type managedValue, IntPtr nativePtr)
|
||||
@@ -1291,14 +1354,14 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
else
|
||||
managedArray = ManagedArrayToGCHandleWrappedArray(arr);
|
||||
managedPtr = ManagedHandle.ToIntPtr(managedArray, GCHandleType.Weak);
|
||||
managedPtr = ManagedHandle.ToIntPtr(managedArray/*, GCHandleType.Weak*/);
|
||||
}
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedPtr);
|
||||
}
|
||||
|
||||
internal static void ToNative(ref T managedValue, IntPtr nativePtr)
|
||||
{
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue, GCHandleType.Weak) : IntPtr.Zero);
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), managedValue != null ? ManagedHandle.ToIntPtr(managedValue/*, GCHandleType.Weak*/) : IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1529,11 +1592,12 @@ namespace FlaxEngine.Interop
|
||||
private static GCHandle[] pinnedBoxedValues = new GCHandle[256];
|
||||
private static uint pinnedBoxedValuesPointer = 0;
|
||||
private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256];
|
||||
private static Action[] pinnedNativeFreeDelegates = new Action[256];
|
||||
private static uint pinnedAllocationsPointer = 0;
|
||||
|
||||
private delegate IntPtr UnboxerDelegate(object value, object converter);
|
||||
private delegate IntPtr UnboxerDelegate(object value, object toNativeConverter, object nativeFree);
|
||||
|
||||
private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object toNativeDeleg)> unboxers = new(1, 3);
|
||||
private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object/*Delegate*/ toNativeConverter, object nativeFree)> unboxers = new(1, 3);
|
||||
private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic);
|
||||
private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic);
|
||||
|
||||
@@ -1546,8 +1610,12 @@ namespace FlaxEngine.Interop
|
||||
var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (toNativeMethod != null)
|
||||
{
|
||||
tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>();
|
||||
tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType));
|
||||
Type internalType = toNativeMethod.ReturnType;
|
||||
tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, internalType).CreateDelegate<UnboxerDelegate>();
|
||||
tuple.toNativeConverter = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, internalType));
|
||||
|
||||
MethodInfo freeNativeMethod = attr.NativeType.GetMethod("Free", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
tuple.nativeFree = freeNativeMethod?.CreateDelegate(typeof(Action<>).MakeGenericType(internalType));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1555,7 +1623,7 @@ namespace FlaxEngine.Interop
|
||||
}
|
||||
tuple = unboxers.GetOrAdd(type, tuple);
|
||||
}
|
||||
return tuple.deleg(value, tuple.toNativeDeleg);
|
||||
return tuple.deleg(value, tuple.toNativeConverter, tuple.nativeFree);
|
||||
}
|
||||
|
||||
private static void PinValue(object value)
|
||||
@@ -1569,12 +1637,15 @@ namespace FlaxEngine.Interop
|
||||
handle = GCHandle.Alloc(value, GCHandleType.Pinned);
|
||||
}
|
||||
|
||||
private static IntPtr PinValue<T>(T value) where T : struct
|
||||
private static IntPtr PinValue<T>(T value, out uint index) where T : struct
|
||||
{
|
||||
// Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
|
||||
int size = TypeHelpers<T>.MarshalSize;
|
||||
uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
|
||||
index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
|
||||
ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
|
||||
Action freeNativeDelegate = pinnedNativeFreeDelegates[index];
|
||||
if (freeNativeDelegate != null)
|
||||
freeNativeDelegate();
|
||||
if (alloc.size < size)
|
||||
{
|
||||
if (alloc.ptr != IntPtr.Zero)
|
||||
@@ -1586,7 +1657,7 @@ namespace FlaxEngine.Interop
|
||||
return alloc.ptr;
|
||||
}
|
||||
|
||||
private static IntPtr UnboxPointer<T>(object value, object converter) where T : struct
|
||||
private static IntPtr UnboxPointer<T>(object value, object toNativeConverter, object nativeFreeDelegate) where T : struct
|
||||
{
|
||||
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) // Cannot pin structure with references
|
||||
return IntPtr.Zero;
|
||||
@@ -1594,11 +1665,16 @@ namespace FlaxEngine.Interop
|
||||
return new IntPtr(Unsafe.AsPointer(ref Unsafe.Unbox<T>(value)));
|
||||
}
|
||||
|
||||
private static IntPtr UnboxPointerWithConverter<T, TInternal>(object value, object converter) where T : struct
|
||||
where TInternal : struct
|
||||
private static IntPtr UnboxPointerWithConverter<T, TInternal>(
|
||||
object value, object nativeConverterDelegate, object nativeFreeDelegate) where T : struct
|
||||
where TInternal : struct
|
||||
{
|
||||
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(converter);
|
||||
return PinValue<TInternal>(toNative(Unsafe.Unbox<T>(value)));
|
||||
ToNativeDelegate<T, TInternal> toNative = Unsafe.As<ToNativeDelegate<T, TInternal>>(nativeConverterDelegate);
|
||||
TInternal nativeValue = toNative(Unsafe.Unbox<T>(value));
|
||||
IntPtr pinnedPtr = PinValue<TInternal>(nativeValue, out uint index);
|
||||
Action<TInternal> freeNative = Unsafe.As<Action<TInternal>>(nativeFreeDelegate);
|
||||
//pinnedNativeFreeDelegates[index] = () => freeNative(nativeValue);
|
||||
return pinnedPtr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1743,25 +1819,37 @@ namespace FlaxEngine.Interop
|
||||
|
||||
internal static void InitMethods()
|
||||
{
|
||||
return;
|
||||
MakeNewCustomDelegateFunc =
|
||||
typeof(Expression).Assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
|
||||
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
|
||||
MakeNewCustomDelegateFunc(new[] { typeof(void) });
|
||||
|
||||
#if FLAX_EDITOR
|
||||
// Load System.Linq.Expressions assembly to collectible ALC.
|
||||
// The dynamic assembly where delegates are stored is cached in the DelegateHelpers class, so we should
|
||||
// use the DelegateHelpers in collectible ALC to make sure the delegates are also stored in the same ALC.
|
||||
Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(typeof(Expression).Assembly.Location);
|
||||
//using var _ = scriptingAssemblyLoadContext.EnterContextualReflection();
|
||||
|
||||
//Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods.");
|
||||
|
||||
var asdfa = AssemblyLoadContext.All.ToArray();
|
||||
|
||||
var asma = typeof(Expression).Assembly;
|
||||
var loc = asma.Location;
|
||||
using FileStream stream = new FileStream(loc, FileMode.Open, FileAccess.Read);
|
||||
Assembly assembly = scriptingAssemblyLoadContext.LoadFromAssemblyPath(loc);
|
||||
MakeNewCustomDelegateFuncCollectible =
|
||||
assembly.GetType("System.Linq.Expressions.Compiler.DelegateHelpers")
|
||||
.GetMethod("MakeNewCustomDelegate", BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate<Func<Type[], Type>>();
|
||||
|
||||
// Create dummy delegates to force the dynamic Snippets assembly to be loaded in correcet ALCs
|
||||
MakeNewCustomDelegateFunc(new[] { typeof(void) });
|
||||
Debug.Logger.LogHandler.LogWrite(LogType.Warning, "InitMethods ok .");
|
||||
|
||||
{
|
||||
// Ensure the new delegate is placed in the collectible ALC
|
||||
// Ensure any future delegates is placed in the collectible ALC
|
||||
using var ctx = scriptingAssemblyLoadContext.EnterContextualReflection();
|
||||
MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) });
|
||||
var ret = MakeNewCustomDelegateFuncCollectible(new[] { typeof(void) });
|
||||
Assert.IsTrue(ret.IsCollectible);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1785,6 +1873,102 @@ namespace FlaxEngine.Interop
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static class ObjectArrayPool
|
||||
{
|
||||
[ThreadStatic]
|
||||
private static List<(bool InUse, object[] Array)> _pool;
|
||||
|
||||
[ThreadStatic]
|
||||
private static List<(bool InUse, object[] Array)> _pool2;
|
||||
|
||||
/// <summary>
|
||||
/// Rents an array from the pool.
|
||||
/// </summary>
|
||||
/// <param name="size">Exact size of the array.</param>
|
||||
internal static object[] Rent(int size)
|
||||
{
|
||||
if (size == 0)
|
||||
return Array.Empty<object>();
|
||||
|
||||
if (_pool == null)
|
||||
_pool = new(16);
|
||||
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
|
||||
{
|
||||
if (tuple.InUse)
|
||||
continue;
|
||||
|
||||
if (tuple.Array.Length != size)
|
||||
continue;
|
||||
|
||||
tuple.InUse = true;
|
||||
return tuple.Array;
|
||||
}
|
||||
|
||||
var newTuple = (InUse: true, Array: new object[size]);
|
||||
_pool.Add(newTuple);
|
||||
return newTuple.Array;
|
||||
}
|
||||
|
||||
internal static object[] Rent2()
|
||||
{
|
||||
if (_pool2 == null)
|
||||
_pool2 = new(16);
|
||||
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool2))
|
||||
{
|
||||
if (tuple.InUse)
|
||||
continue;
|
||||
|
||||
tuple.InUse = true;
|
||||
return tuple.Array;
|
||||
}
|
||||
|
||||
var newTuple = (InUse: true, Array: new object[8]);
|
||||
_pool2.Add(newTuple);
|
||||
return newTuple.Array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the rented object array back to the pool.
|
||||
/// </summary>
|
||||
/// <param name="array">The array rented from the pool.</param>
|
||||
internal static void Return(object[] array)
|
||||
{
|
||||
if (array.Length == 0)
|
||||
return;
|
||||
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool))
|
||||
{
|
||||
if (tuple.Array != array)
|
||||
continue;
|
||||
|
||||
tuple.InUse = false;
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
array[i] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NativeInteropException("Tried to free non-pooled object array as pooled ManagedArray");
|
||||
}
|
||||
|
||||
internal static void Return2(object[] array)
|
||||
{
|
||||
foreach (ref var tuple in CollectionsMarshal.AsSpan(_pool2))
|
||||
{
|
||||
if (tuple.Array != array)
|
||||
continue;
|
||||
|
||||
tuple.InUse = false;
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
array[i] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NativeInteropException("Tried to free non-pooled object array as pooled ManagedArray");
|
||||
}
|
||||
}
|
||||
|
||||
#if !USE_AOT
|
||||
/// <summary>
|
||||
/// Wrapper class for invoking function pointers from unmanaged code.
|
||||
@@ -1792,13 +1976,18 @@ namespace FlaxEngine.Interop
|
||||
internal class ThunkContext
|
||||
{
|
||||
internal MethodInfo method;
|
||||
internal MethodInvoker invoker;
|
||||
internal Type[] parameterTypes;
|
||||
internal Invoker.InvokeThunkDelegate methodDelegate;
|
||||
internal object methodDelegateContext;
|
||||
|
||||
internal static object[] objectPool = new object[128];
|
||||
internal static int objectPoolIndex = 0;
|
||||
|
||||
internal ThunkContext(MethodInfo method)
|
||||
{
|
||||
this.method = method;
|
||||
invoker = MethodInvoker.Create(method);
|
||||
parameterTypes = method.GetParameterTypes();
|
||||
|
||||
// Thunk delegates don't support IsByRef parameters (use generic invocation that handles 'out' and 'ref' prams)
|
||||
@@ -1826,6 +2015,7 @@ namespace FlaxEngine.Interop
|
||||
genericParamTypes.Add(type);
|
||||
}
|
||||
|
||||
#if false
|
||||
string invokerTypeName = $"{typeof(Invoker).FullName}+Invoker{(method.IsStatic ? "Static" : "")}{(method.ReturnType != typeof(void) ? "Ret" : "NoRet")}{parameterTypes.Length}{(genericParamTypes.Count > 0 ? "`" + genericParamTypes.Count : "")}";
|
||||
Type invokerType = Type.GetType(invokerTypeName);
|
||||
if (invokerType != null)
|
||||
@@ -1838,11 +2028,13 @@ namespace FlaxEngine.Interop
|
||||
|
||||
if (methodDelegate != null)
|
||||
Assert.IsTrue(methodDelegateContext != null);
|
||||
#endif
|
||||
}
|
||||
|
||||
public IntPtr InvokeThunk(ManagedHandle instanceHandle, IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4, IntPtr param5, IntPtr param6, IntPtr param7)
|
||||
{
|
||||
IntPtr* nativePtrs = stackalloc IntPtr[] { param1, param2, param3, param4, param5, param6, param7 };
|
||||
#if false
|
||||
if (methodDelegate != null)
|
||||
{
|
||||
IntPtr returnValue;
|
||||
@@ -1861,36 +2053,69 @@ namespace FlaxEngine.Interop
|
||||
return returnValue;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// The parameters are wrapped (boxed) in GCHandles
|
||||
object returnObject;
|
||||
int numParams = parameterTypes.Length;
|
||||
object[] methodParameters = new object[numParams];
|
||||
//object[] methodParameters = ObjectArrayPool.Rent(numParams);//new object[numParams]; // TODO: object array pool
|
||||
if (objectPoolIndex + numParams > 128)
|
||||
objectPoolIndex = 0;
|
||||
var paramSpan = objectPool.AsSpan(objectPoolIndex, numParams);
|
||||
objectPoolIndex += numParams;
|
||||
for (int i = 0; i < numParams; i++)
|
||||
{
|
||||
IntPtr nativePtr = nativePtrs[i];
|
||||
object managed = null;
|
||||
if (nativePtr != IntPtr.Zero)
|
||||
{
|
||||
object managed = null;
|
||||
Type type = parameterTypes[i];
|
||||
Type elementType = type.GetElementType();
|
||||
if (type.IsByRef)
|
||||
{
|
||||
// References use indirection to support value returning
|
||||
nativePtr = Unsafe.Read<IntPtr>(nativePtr.ToPointer());
|
||||
type = elementType;
|
||||
type = type.GetElementType();
|
||||
}
|
||||
if (type.IsArray)
|
||||
managed = MarshalToManaged(nativePtr, type); // Array might be in internal format of custom structs so unbox if need to
|
||||
else if (type == typeof(Type))
|
||||
managed = Unsafe.As<TypeHolder>(ManagedHandle.FromIntPtr(nativePtr).Target).type;
|
||||
else
|
||||
managed = ManagedHandle.FromIntPtr(nativePtr).Target;
|
||||
paramSpan[i] = managed;
|
||||
}
|
||||
methodParameters[i] = managed;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
returnObject = method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
|
||||
returnObject = invoker.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, paramSpan);
|
||||
//returnObject = method.Invoke(instanceHandle.IsAllocated ? instanceHandle.Target : null, methodParameters);
|
||||
|
||||
// Marshal reference parameters back to original unmanaged references
|
||||
for (int i = 0; i < numParams; i++)
|
||||
{
|
||||
IntPtr nativePtr = nativePtrs[i];
|
||||
Type type = parameterTypes[i];
|
||||
if (nativePtr != IntPtr.Zero && type.IsByRef)
|
||||
{
|
||||
object managed = paramSpan[i];
|
||||
type = type.GetElementType();
|
||||
if (managed == null)
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), IntPtr.Zero);
|
||||
else if (type.IsArray)
|
||||
MarshalToNative(managed, nativePtr, type);
|
||||
else
|
||||
{
|
||||
var ptr = nativePtr.ToPointer();
|
||||
if (type == typeof(Type))
|
||||
managed = GetTypeHolder(Unsafe.As<Type>(managed));
|
||||
var handle = ManagedHandle.Alloc(managed/*, GCHandleType.Weak*/);
|
||||
var handleptr = ManagedHandle.ToIntPtr(handle);
|
||||
Unsafe.Write<IntPtr>(ptr, handleptr);
|
||||
//Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -1900,32 +2125,19 @@ namespace FlaxEngine.Interop
|
||||
Unsafe.Write<IntPtr>(exceptionPtr.ToPointer(), ManagedHandle.ToIntPtr(exception, GCHandleType.Weak));
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
// Marshal reference parameters back to original unmanaged references
|
||||
for (int i = 0; i < numParams; i++)
|
||||
finally
|
||||
{
|
||||
IntPtr nativePtr = nativePtrs[i];
|
||||
Type type = parameterTypes[i];
|
||||
object managed = methodParameters[i];
|
||||
if (nativePtr != IntPtr.Zero && type.IsByRef)
|
||||
{
|
||||
type = type.GetElementType();
|
||||
if (managed == null)
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), IntPtr.Zero);
|
||||
else if (type.IsArray)
|
||||
MarshalToNative(managed, nativePtr, type);
|
||||
else
|
||||
Unsafe.Write<IntPtr>(nativePtr.ToPointer(), ManagedHandle.ToIntPtr(ManagedHandle.Alloc(managed, GCHandleType.Weak)));
|
||||
}
|
||||
//ObjectArrayPool.Return(methodParameters);
|
||||
for (int i = 0; i < numParams; i++)
|
||||
paramSpan[i] = null;
|
||||
}
|
||||
|
||||
// Return value
|
||||
return Invoker.MarshalReturnValueThunkGeneric(method.ReturnType, returnObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal class NativeInteropException : Exception
|
||||
{
|
||||
|
||||
@@ -450,7 +450,7 @@ public:
|
||||
/// <summary>
|
||||
/// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering.
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct RenderContext
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext);
|
||||
|
||||
@@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext
|
||||
/// <summary>
|
||||
/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once).
|
||||
/// </summary>
|
||||
API_STRUCT(NoDefault) struct RenderContextBatch
|
||||
API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch);
|
||||
|
||||
|
||||
@@ -553,7 +553,7 @@ void GPUTextureDX11::initHandles()
|
||||
if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
|
||||
{
|
||||
PixelFormat stencilFormat;
|
||||
switch (_dxgiFormatDSV)
|
||||
switch (static_cast<PixelFormat>(_dxgiFormatDSV))
|
||||
{
|
||||
case PixelFormat::D24_UNorm_S8_UInt:
|
||||
srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
|
||||
|
||||
@@ -210,7 +210,7 @@ public:
|
||||
/// <param name="device">The graphics device.</param>
|
||||
/// <param name="name">The resource name.</param>
|
||||
GPUResourceDX12(GPUDeviceDX12* device, const StringView& name)
|
||||
: GPUResourceBase(device, name)
|
||||
: GPUResourceBase<GPUDeviceDX12, BaseType>(device, name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -732,7 +732,7 @@ void GPUTextureDX12::initHandles()
|
||||
if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format))
|
||||
{
|
||||
PixelFormat stencilFormat;
|
||||
switch (_dxgiFormatDSV)
|
||||
switch (static_cast<PixelFormat>(_dxgiFormatDSV))
|
||||
{
|
||||
case PixelFormat::D24_UNorm_S8_UInt:
|
||||
srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
|
||||
|
||||
@@ -194,7 +194,8 @@ CultureInfo MUtils::ToNative(void* value)
|
||||
if (lcidProperty && lcidProperty->GetGetMethod())
|
||||
{
|
||||
MObject* lcidObj = lcidProperty->GetGetMethod()->Invoke(value, nullptr, nullptr);
|
||||
lcid = *(int32*)MCore::Object::Unbox(lcidObj);
|
||||
PLATFORM_DEBUG_BREAK;
|
||||
MCore::Object::Unbox(lcidObj, &lcid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
MObject* exception = nullptr; \
|
||||
auto resultObj = _method_##name->Invoke(GetManagedInstance(), params, &exception); \
|
||||
if (resultObj) \
|
||||
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj); \
|
||||
result = (DragDropEffect)MUtils::Unbox<int32>(resultObj, true); \
|
||||
END_INVOKE_EVENT(name)
|
||||
#else
|
||||
#define INVOKE_EVENT(name, paramsCount, param0, param1, param2)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Engine/Platform/CreateProcessSettings.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/SDL/SDLInput.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
@@ -317,11 +318,11 @@ Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
return New<SDLWindow>(settings);
|
||||
}
|
||||
|
||||
bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32& bufferPosition, LogType logType, CreateProcessSettings& settings)
|
||||
bool ReadStream(SDL_IOStream*& stream, char* buffer, int64 bufferLength, int64& bufferPosition, LogType logType, CreateProcessSettings& settings)
|
||||
{
|
||||
bool flushBuffer = false;
|
||||
bool success = true;
|
||||
int32 read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1);
|
||||
auto read = SDL_ReadIO(stream, buffer + bufferPosition, bufferLength - bufferPosition - 1);
|
||||
if (read == 0)
|
||||
{
|
||||
SDL_IOStatus status = SDL_GetIOStatus(stream);
|
||||
@@ -335,8 +336,8 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
|
||||
}
|
||||
else
|
||||
{
|
||||
int32 startPosition = bufferPosition;
|
||||
bufferPosition += read;
|
||||
int64 startPosition = bufferPosition;
|
||||
bufferPosition += (int64)read;
|
||||
if (bufferPosition == bufferLength - 1)
|
||||
{
|
||||
flushBuffer = true;
|
||||
@@ -344,7 +345,7 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = startPosition; i < bufferPosition; ++i)
|
||||
for (int64 i = startPosition; i < bufferPosition; ++i)
|
||||
{
|
||||
if (buffer[i] == '\n')
|
||||
{
|
||||
@@ -357,13 +358,13 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
|
||||
|
||||
if (flushBuffer)
|
||||
{
|
||||
int32 start = 0;
|
||||
for (int i = 0; i < bufferPosition; ++i)
|
||||
int64 start = 0;
|
||||
for (int64 i = 0; i < bufferPosition; ++i)
|
||||
{
|
||||
if (buffer[i] != '\n')
|
||||
continue;
|
||||
|
||||
String str(&buffer[start], i - start + 1);
|
||||
String str(&buffer[start], (int32)(i - start + 1));
|
||||
#if LOG_ENABLE
|
||||
if (settings.LogOutput)
|
||||
Log::Logger::Write(logType, StringView(str.Get(), str.Length() - 1));
|
||||
@@ -372,7 +373,7 @@ bool ReadStream(SDL_IOStream*& stream, char* buffer, int32 bufferLength, int32&
|
||||
settings.Output.Add(str.Get(), str.Length());
|
||||
start = i + 1;
|
||||
}
|
||||
int32 length = bufferPosition - start;
|
||||
int64 length = bufferPosition - start;
|
||||
if (length > 0)
|
||||
{
|
||||
// TODO: Use memmove here? Overlapped memory regions with memcpy is undefined behaviour
|
||||
@@ -396,13 +397,7 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
int32 result = 0;
|
||||
const bool captureStdOut = settings.LogOutput || settings.SaveOutput;
|
||||
const StringAnsi cmdLine = StringAnsi::Format("\"{0}\" {1}", StringAnsi(settings.FileName), StringAnsi(settings.Arguments));
|
||||
const char* cmd[] = { "sh", "-c", cmdLine.Get(), (char *)0 };
|
||||
StringAnsi workingDirectory(settings.WorkingDirectory);
|
||||
#if PLATFORM_WINDOWS
|
||||
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
|
||||
#else
|
||||
bool background = !settings.WaitForEnd;
|
||||
#endif
|
||||
|
||||
// Populate environment with current values from parent environment.
|
||||
// SDL does not populate the environment with the latest values but with a snapshot captured during initialization.
|
||||
@@ -414,8 +409,27 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
for (auto iter = settings.Environment.Begin(); iter != settings.Environment.End(); ++iter)
|
||||
SDL_SetEnvironmentVariable(env, StringAnsi(iter->Key).Get(), StringAnsi(iter->Value).Get(), true);
|
||||
|
||||
// Parse argument list with possible quotes included
|
||||
Array<StringAnsi> arguments;
|
||||
arguments.Add(StringAnsi(settings.FileName));
|
||||
if (CommandLine::ParseArguments(settings.Arguments, arguments))
|
||||
{
|
||||
LOG(Error, "Failed to parse arguments for process {}: '{}'", settings.FileName.Get(), settings.Arguments.Get());
|
||||
return -1;
|
||||
}
|
||||
Array<const char*> cmd;
|
||||
for (const StringAnsi& str : arguments)
|
||||
cmd.Add(str.Get());
|
||||
cmd.Add((const char*)0);
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
bool background = !settings.WaitForEnd || settings.HiddenWindow; // This also hides the window on Windows
|
||||
#else
|
||||
bool background = !settings.WaitForEnd;
|
||||
#endif
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd);
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get());
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background);
|
||||
if (workingDirectory.HasChars())
|
||||
@@ -430,12 +444,12 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
SDL_DestroyEnvironment(env);
|
||||
if (process == nullptr)
|
||||
{
|
||||
LOG(Error, "Failed to run process {}: {}", String(settings.FileName).Get(), String(SDL_GetError()));
|
||||
LOG(Error, "Failed to run process {}: {}", settings.FileName.Get(), String(SDL_GetError()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
props = SDL_GetProcessProperties(process);
|
||||
int32 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
|
||||
int64 pid = SDL_GetNumberProperty(props, SDL_PROP_PROCESS_PID_NUMBER, 0);
|
||||
SDL_IOStream* stdoutStream = nullptr;
|
||||
SDL_IOStream* stderrStream = nullptr;
|
||||
if (captureStdOut)
|
||||
@@ -446,9 +460,9 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
|
||||
// Handle process output in realtime
|
||||
char stdoutBuffer[2049];
|
||||
int32 stdoutPosition = 0;
|
||||
int64 stdoutPosition = 0;
|
||||
char stderrBuffer[2049];
|
||||
int32 stderrPosition = 0;
|
||||
int64 stderrPosition = 0;
|
||||
while (stdoutStream != nullptr && stderrStream != nullptr)
|
||||
{
|
||||
if (stdoutStream != nullptr && !ReadStream(stdoutStream, stdoutBuffer, sizeof(stdoutBuffer), stdoutPosition, LogType::Info, settings))
|
||||
|
||||
@@ -1332,7 +1332,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
}
|
||||
|
||||
// Unbox result
|
||||
result = MUtils::UnboxVariant(resultObject);
|
||||
result = MUtils::UnboxVariant(resultObject, true);
|
||||
|
||||
#if 0
|
||||
// Helper method invocations logging
|
||||
@@ -1366,7 +1366,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
paramValue.SetString(MUtils::ToString((MString*)param));
|
||||
break;
|
||||
case VariantType::Object:
|
||||
paramValue = MUtils::UnboxVariant((MObject*)param);
|
||||
paramValue = MUtils::UnboxVariant((MObject*)param, true);
|
||||
break;
|
||||
case VariantType::Structure:
|
||||
{
|
||||
@@ -1383,7 +1383,7 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
{
|
||||
MType* paramType = mMethod->GetParameterType(paramIdx);
|
||||
if (MCore::Type::IsReference(paramType) && MCore::Type::GetType(paramType) == MTypes::Object)
|
||||
paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx]);
|
||||
paramValue = MUtils::UnboxVariant((MObject*)outParams[paramIdx], true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1527,7 +1527,7 @@ bool ManagedBinaryModule::GetFieldValue(void* field, const Variant& instance, Va
|
||||
const auto mField = (MField*)field;
|
||||
resultObject = mField->GetValueBoxed(instanceObject);
|
||||
}
|
||||
result = MUtils::UnboxVariant(resultObject);
|
||||
result = MUtils::UnboxVariant(resultObject, true);
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
|
||||
@@ -68,12 +68,13 @@ public:
|
||||
struct FLAXENGINE_API Object
|
||||
{
|
||||
static MObject* Box(void* value, const MClass* klass);
|
||||
static void* Unbox(MObject* obj);
|
||||
static void* Unbox(const MObject* obj);
|
||||
static void Unbox(const MObject* obj, void* dest);
|
||||
static MObject* New(const MClass* klass);
|
||||
static void Init(MObject* obj);
|
||||
static MClass* GetClass(MObject* obj);
|
||||
static MString* ToString(MObject* obj);
|
||||
static int32 GetHashCode(MObject* obj);
|
||||
static void Init(const MObject* obj);
|
||||
static MClass* GetClass(const MObject* obj);
|
||||
static MString* ToString(const MObject* obj);
|
||||
static int32 GetHashCode(const MObject* obj);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -81,10 +82,11 @@ public:
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API String
|
||||
{
|
||||
static MString* GetEmpty(MDomain* domain = nullptr);
|
||||
static MString* New(const char* str, int32 length, MDomain* domain = nullptr);
|
||||
static MString* New(const Char* str, int32 length, MDomain* domain = nullptr);
|
||||
static StringView GetChars(MString* obj);
|
||||
static MString* GetEmpty(const MDomain* domain = nullptr);
|
||||
static MString* New(const char* str, int32 length, const MDomain* domain = nullptr);
|
||||
static MString* New(const Char* str, int32 length, const MDomain* domain = nullptr);
|
||||
static void Free(const MString* obj);
|
||||
static StringView GetChars(const MString* obj);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -93,11 +95,12 @@ public:
|
||||
struct FLAXENGINE_API Array
|
||||
{
|
||||
static MArray* New(const MClass* elementKlass, int32 length);
|
||||
static MClass* GetClass(MClass* elementKlass);
|
||||
static void Free(const MArray* array);
|
||||
static MClass* GetClass(const MClass* elementKlass);
|
||||
static MClass* GetArrayClass(const MArray* obj);
|
||||
static int32 GetLength(const MArray* obj);
|
||||
static void* GetAddress(const MArray* obj);
|
||||
static MArray* Unbox(MObject* obj);
|
||||
static MArray* Unbox(const MObject* obj);
|
||||
|
||||
template<typename T>
|
||||
FORCE_INLINE static T* GetAddress(const MArray* obj)
|
||||
@@ -111,8 +114,8 @@ public:
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API GCHandle
|
||||
{
|
||||
static MGCHandle New(MObject* obj, bool pinned = false);
|
||||
static MGCHandle NewWeak(MObject* obj, bool trackResurrection = false);
|
||||
static MGCHandle New(const MObject* obj, bool pinned = false);
|
||||
static MGCHandle NewWeak(const MObject* obj, bool trackResurrection = false);
|
||||
static MObject* GetTarget(const MGCHandle& handle);
|
||||
static void Free(const MGCHandle& handle);
|
||||
};
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
/// </remarks>
|
||||
/// <param name="instance">The object of given type to get value from.</param>
|
||||
/// <param name="result">The return value of undefined type.</param>
|
||||
void GetValue(MObject* instance, void* result) const;
|
||||
void GetValue(const MObject* instance, void* result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null.
|
||||
@@ -116,14 +116,14 @@ public:
|
||||
/// </remarks>
|
||||
/// <param name="instance">The object of given type to get value from.</param>
|
||||
/// <param name="result">The return value of undefined type.</param>
|
||||
void GetValueReference(MObject* instance, void* result) const;
|
||||
void GetValueReference(const MObject* instance, void* result) const;
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves value currently set in the field on the specified object instance. If field is static object instance can be null. If returned value is a value type it will be boxed.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object of given type to get value from.</param>
|
||||
/// <returns>The boxed value object.</returns>
|
||||
MObject* GetValueBoxed(MObject* instance) const;
|
||||
MObject* GetValueBoxed(const MObject* instance) const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets a value for the field on the specified object instance. If field is static object instance can be null.
|
||||
@@ -133,7 +133,7 @@ public:
|
||||
/// </remarks>
|
||||
/// <param name="instance">The object of given type to set value to.</param>
|
||||
/// <param name="value">Th value of undefined type.</param>
|
||||
void SetValue(MObject* instance, void* value) const;
|
||||
void SetValue(const MObject* instance, void* value) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -304,7 +304,7 @@ MTypeObject* MUtils::BoxVariantType(const VariantType& value)
|
||||
return INTERNAL_TYPE_GET_OBJECT(mType);
|
||||
}
|
||||
|
||||
Variant MUtils::UnboxVariant(MObject* value)
|
||||
Variant MUtils::UnboxVariant(MObject* value, bool releaseHandle)
|
||||
{
|
||||
if (value == nullptr)
|
||||
return Variant::Null;
|
||||
@@ -313,7 +313,10 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
|
||||
MType* mType = klass->GetType();
|
||||
const MTypes mTypes = MCore::Type::GetType(mType);
|
||||
void* unboxed = MCore::Object::Unbox(value);
|
||||
|
||||
Variant storage;
|
||||
MCore::Object::Unbox(value, &storage.AsData);
|
||||
void* unboxed = &storage.AsData;
|
||||
|
||||
// Fast type detection for in-built types
|
||||
switch (mTypes)
|
||||
@@ -345,7 +348,11 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
case MTypes::R8:
|
||||
return *static_cast<double*>(unboxed);
|
||||
case MTypes::String:
|
||||
{
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<String>(value);
|
||||
return Variant(MUtils::ToString((MString*)value));
|
||||
}
|
||||
case MTypes::Ptr:
|
||||
return *static_cast<void**>(unboxed);
|
||||
case MTypes::ValueType:
|
||||
@@ -400,6 +407,8 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
{
|
||||
Variant v;
|
||||
v.SetBlob(ptr, MCore::Array::GetLength((MArray*)value));
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<Array<byte>>(value);
|
||||
return v;
|
||||
}
|
||||
const StringAnsiView fullname = arrayClass->GetFullName();
|
||||
@@ -486,13 +495,16 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
// TODO: optimize this for large arrays to prevent multiple AllocStructure calls in Variant::SetType by using computed struct type
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
auto& a = array[i];
|
||||
a.SetType(elementType);
|
||||
void* managed = (byte*)ptr + elementSize * i;
|
||||
// TODO: optimize structures unboxing to not require MObject* but raw managed value data to prevent additional boxing here
|
||||
MObject* boxed = MCore::Object::New(elementClass);
|
||||
Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize);
|
||||
type.Struct.Unbox(a.AsBlob.Data, boxed);
|
||||
//MObject* boxed = MCore::Object::New(elementClass);
|
||||
//Variant storage;
|
||||
MCore::Object::Unbox((MObject*)managed, &a.AsBlob.Data);
|
||||
//Platform::MemoryCopy(MCore::Object::Unbox(boxed), managed, elementSize);
|
||||
//type.Struct.Unbox(a.AsBlob.Data, boxed);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -508,7 +520,10 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
{
|
||||
// Array of Objects
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
array[i] = UnboxVariant(((MObject**)ptr)[i]);
|
||||
array[i] = UnboxVariant(((MObject**)ptr)[i], releaseHandle);
|
||||
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<Array<byte>>(value);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@@ -527,10 +542,13 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
{
|
||||
MObject* keyManaged = managedKeysPtr[i];
|
||||
MObject* valueManaged = managed.GetValue(keyManaged);
|
||||
native.Add(UnboxVariant(keyManaged), UnboxVariant(valueManaged));
|
||||
native.Add(UnboxVariant(keyManaged, releaseHandle), UnboxVariant(valueManaged, releaseHandle));
|
||||
}
|
||||
Variant v(MoveTemp(native));
|
||||
v.Type.SetTypeName(klass->GetFullName());
|
||||
if (releaseHandle)
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&value);
|
||||
//MUtils::FreeManaged<Dictionary<byte, byte>>(value);
|
||||
return v;
|
||||
}
|
||||
break;
|
||||
@@ -547,7 +565,10 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
Variant v;
|
||||
v.Type = MoveTemp(VariantType(VariantType::Enum, fullname));
|
||||
// TODO: what about 64-bit enum? use enum size with memcpy
|
||||
v.AsUint64 = *static_cast<uint32*>(MCore::Object::Unbox(value));
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Object::Unbox(value, &v.AsUint64);
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<byte>(value);
|
||||
return v;
|
||||
}
|
||||
if (klass->IsValueType())
|
||||
@@ -565,10 +586,12 @@ Variant MUtils::UnboxVariant(MObject* value)
|
||||
type.Struct.Unbox(v.AsBlob.Data, value);
|
||||
return v;
|
||||
}
|
||||
return Variant(value);
|
||||
}
|
||||
|
||||
return Variant(value);
|
||||
auto variant = Variant(value);
|
||||
if (releaseHandle)
|
||||
MUtils::FreeManaged<byte>(value);
|
||||
return variant;
|
||||
}
|
||||
|
||||
MObject* MUtils::BoxVariant(const Variant& value)
|
||||
@@ -727,9 +750,10 @@ MObject* MUtils::BoxVariant(const Variant& value)
|
||||
ASSERT(type.Type == ScriptingTypes::Structure);
|
||||
for (int32 i = 0; i < array.Count(); i++)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
// TODO: optimize structures boxing to not return MObject* but use raw managed object to prevent additional boxing here
|
||||
MObject* boxed = type.Struct.Box(array[i].AsBlob.Data);
|
||||
Platform::MemoryCopy(managedPtr + elementSize * i, MCore::Object::Unbox(boxed), elementSize);
|
||||
MCore::Object::Unbox(boxed, managedPtr + elementSize * i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1160,7 +1184,9 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
auto& valueType = typeHandle.GetType();
|
||||
if (valueType.ManagedClass == MCore::Type::GetClass(type))
|
||||
{
|
||||
return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data));
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data), &value.AsBlob.Data);
|
||||
return &value.AsBlob.Data;
|
||||
}
|
||||
LOG(Error, "Cannot marshal argument of type {0} as {1}", String(valueType.Fullname), MCore::Type::ToString(type));
|
||||
}
|
||||
@@ -1171,9 +1197,11 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, MType* type, bool& failed)
|
||||
const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(fullname);
|
||||
if (typeHandle)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
auto& valueType = typeHandle.GetType();
|
||||
value.SetType(VariantType(VariantType::Structure, fullname));
|
||||
return MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data));
|
||||
MCore::Object::Unbox(valueType.Struct.Box(value.AsBlob.Data), &value.AsBlob.Data);
|
||||
return &value.AsBlob.Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
|
||||
#if USE_CSHARP
|
||||
|
||||
/*static_assert(TIsPODType<int>::Value, "int not pod type?");
|
||||
static_assert(TIsPODType<Float3>::Value, "Float3 not pod type?");
|
||||
static_assert(!TIsPODType<ScriptingObject>::Value, "ScriptingObject not pod type?");
|
||||
static_assert(TIsPODType<Char>::Value, "Char not pod type?");
|
||||
static_assert(TIsPODType<char>::Value, "char not pod type?");*/
|
||||
|
||||
struct Version;
|
||||
class CultureInfo;
|
||||
template<typename AllocationType>
|
||||
@@ -39,7 +45,7 @@ namespace MUtils
|
||||
extern FLAXENGINE_API MTypeObject* BoxScriptingTypeHandle(const ScriptingTypeHandle& value);
|
||||
extern FLAXENGINE_API VariantType UnboxVariantType(MType* type);
|
||||
extern FLAXENGINE_API MTypeObject* BoxVariantType(const VariantType& value);
|
||||
extern FLAXENGINE_API Variant UnboxVariant(MObject* value);
|
||||
extern FLAXENGINE_API Variant UnboxVariant(MObject* value, bool releaseHandle = false);
|
||||
extern FLAXENGINE_API MObject* BoxVariant(const Variant& value);
|
||||
}
|
||||
|
||||
@@ -49,10 +55,84 @@ struct MConverter
|
||||
{
|
||||
MObject* Box(const T& data, const MClass* klass);
|
||||
void Unbox(T& result, MObject* data);
|
||||
/*template<typename U>
|
||||
void ToManaged(U& result, const T& data);
|
||||
template<typename U>
|
||||
void ToNative(T& result, const U& data);
|
||||
void ToManaged(MObject*& result, const T& data);
|
||||
void ToNative(T& result, const MObject*& data);*/
|
||||
void FreeManaged(MObject* data);
|
||||
void ToManagedArray(MArray* result, const Span<T>& data);
|
||||
void ToNativeArray(Span<T>& result, const MArray* data);
|
||||
void FreeManagedArray(MArray* array);
|
||||
};
|
||||
|
||||
// Simple array used to pass arrays of blittable data between managed and unmanaged side.
|
||||
template<typename T>
|
||||
struct NativeArray
|
||||
{
|
||||
T* data;
|
||||
int32 length;
|
||||
|
||||
FORCE_INLINE operator T*() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
FORCE_INLINE Span<T> ToSpan() const
|
||||
{
|
||||
return Span<T>(data, length);
|
||||
}
|
||||
};
|
||||
|
||||
#if USE_NETCORE
|
||||
// Special case for boolean values, the handles are fixed and should not be removed
|
||||
template<>
|
||||
struct MConverter<bool>
|
||||
{
|
||||
MObject* Box(const bool& data, const MClass* klass)
|
||||
{
|
||||
return MCore::Object::Box((void*)&data, klass);
|
||||
}
|
||||
|
||||
void Unbox(bool& result, MObject* data)
|
||||
{
|
||||
if (data)
|
||||
MCore::Object::Unbox(data, &result);
|
||||
}
|
||||
|
||||
void ToManaged(bool& result, const bool& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(bool& result, const bool& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<bool>& data)
|
||||
{
|
||||
Platform::MemoryCopy(MCore::Array::GetAddress(result), data.Get(), data.Length() * sizeof(bool));
|
||||
}
|
||||
|
||||
void ToNativeArray(Span<bool>& result, const MArray* data)
|
||||
{
|
||||
Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(bool));
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
// Converter for POD types (that can use raw memory copy).
|
||||
template<typename T>
|
||||
struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class ScriptingObject, typename TRemovePointer<T>::Type>>>::Value>::Type>
|
||||
@@ -65,7 +145,17 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
|
||||
void Unbox(T& result, MObject* data)
|
||||
{
|
||||
if (data)
|
||||
Platform::MemoryCopy(&result, MCore::Object::Unbox(data), sizeof(T));
|
||||
MCore::Object::Unbox(data, &result);
|
||||
}
|
||||
|
||||
void ToManaged(T& result, const T& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(T& result, const T& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<T>& data)
|
||||
@@ -77,6 +167,17 @@ struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class
|
||||
{
|
||||
Platform::MemoryCopy(result.Get(), MCore::Array::GetAddress(data), result.Length() * sizeof(T));
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&data);
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for String.
|
||||
@@ -85,22 +186,32 @@ struct MConverter<String>
|
||||
{
|
||||
MObject* Box(const String& data, const MClass* klass)
|
||||
{
|
||||
#if USE_NETCORE
|
||||
/*#if USE_NETCORE
|
||||
MString* str = MUtils::ToString(data);
|
||||
return MCore::Object::Box(str, klass);
|
||||
#else
|
||||
#else*/
|
||||
return (MObject*)MUtils::ToString(data);
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
void Unbox(String& result, MObject* data)
|
||||
{
|
||||
#if USE_NETCORE
|
||||
/*#if USE_NETCORE
|
||||
MString* str = (MString*)MCore::Object::Unbox(data);
|
||||
result = MUtils::ToString(str);
|
||||
#else
|
||||
#else*/
|
||||
result = MUtils::ToString((MString*)data);
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
|
||||
void ToManaged(String& result, const String& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(String& result, const String& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<String>& data)
|
||||
@@ -120,6 +231,23 @@ struct MConverter<String>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
//MString* str = (MString*)MCore::Object::Unbox(data);
|
||||
//MCore::GCHandle::Free(*(MGCHandle*)&str);
|
||||
MCore::String::Free((MString*)data);
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(array);
|
||||
auto length = MCore::Array::GetLength(array);
|
||||
for (int32 i = 0; i < length; i++)
|
||||
MCore::String::Free(dataPtr[i]);
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for StringAnsi.
|
||||
@@ -136,6 +264,16 @@ struct MConverter<StringAnsi>
|
||||
result = MUtils::ToStringAnsi((MString*)data);
|
||||
}
|
||||
|
||||
void ToManaged(StringAnsi& result, const StringAnsi& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(StringAnsi& result, const StringAnsi& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<StringAnsi>& data)
|
||||
{
|
||||
if (data.Length() == 0)
|
||||
@@ -153,6 +291,21 @@ struct MConverter<StringAnsi>
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
MCore::String::Free((MString*)data);
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(array);
|
||||
auto length = MCore::Array::GetLength(array);
|
||||
for (int32 i = 0; i < length; i++)
|
||||
MCore::String::Free(dataPtr[i]);
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for StringView.
|
||||
@@ -161,16 +314,29 @@ struct MConverter<StringView>
|
||||
{
|
||||
MObject* Box(const StringView& data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
return (MObject*)MUtils::ToString(data);
|
||||
}
|
||||
|
||||
void Unbox(StringView& result, MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = MUtils::ToString((MString*)data);
|
||||
}
|
||||
|
||||
void ToManaged(StringView& result, const StringView& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(StringView& result, const StringView& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<StringView>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
if (data.Length() == 0)
|
||||
return;
|
||||
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
|
||||
@@ -182,10 +348,28 @@ struct MConverter<StringView>
|
||||
|
||||
void ToNativeArray(Span<StringView>& result, const MArray* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
MUtils::ToString(dataPtr[i], result.Get()[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::String::Free((MString*)data);
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MString** dataPtr = MCore::Array::GetAddress<MString*>(array);
|
||||
auto length = MCore::Array::GetLength(array);
|
||||
for (int32 i = 0; i < length; i++)
|
||||
MCore::String::Free(dataPtr[i]);
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Variant.
|
||||
@@ -194,16 +378,29 @@ struct MConverter<Variant>
|
||||
{
|
||||
MObject* Box(const Variant& data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
return MUtils::BoxVariant(data);
|
||||
}
|
||||
|
||||
void Unbox(Variant& result, MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = MUtils::UnboxVariant(data);
|
||||
}
|
||||
|
||||
void ToManaged(Variant& result, const Variant& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(Variant& result, const Variant& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<Variant>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
if (data.Length() == 0)
|
||||
return;
|
||||
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
|
||||
@@ -215,10 +412,23 @@ struct MConverter<Variant>
|
||||
|
||||
void ToNativeArray(Span<Variant>& result, const MArray* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = MUtils::UnboxVariant(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Scripting Objects (collection of pointers).
|
||||
@@ -227,11 +437,25 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
|
||||
{
|
||||
MObject* Box(T* data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
return data ? data->GetOrCreateManagedInstance() : nullptr;
|
||||
}
|
||||
|
||||
void Unbox(T*& result, MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = (T*)ScriptingObject::ToNative(data);
|
||||
}
|
||||
|
||||
void ToManaged(MObject*& result, const T* data)
|
||||
{
|
||||
//PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = data ? data->GetOrCreateManagedInstance() : nullptr;
|
||||
}
|
||||
|
||||
void ToNative(T*& result, const MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = (T*)ScriptingObject::ToNative(data);
|
||||
}
|
||||
|
||||
@@ -252,6 +476,17 @@ struct MConverter<T*, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Va
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Scripting Objects (collection of values).
|
||||
@@ -260,11 +495,33 @@ struct MConverter<T, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Val
|
||||
{
|
||||
MObject* Box(const T& data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
return data.GetOrCreateManagedInstance();
|
||||
}
|
||||
|
||||
void ToManaged(MObject*& result, const T& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(T& result, const MObject*& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
/*void ToManaged(MObject*& result, const T*& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(T*& result, const MObject*& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}*/
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<T>& data)
|
||||
{
|
||||
//PLATFORM_DEBUG_BREAK; // FIXME
|
||||
if (data.Length() == 0)
|
||||
return;
|
||||
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
|
||||
@@ -273,6 +530,18 @@ struct MConverter<T, typename TEnableIf<TIsBaseOf<class ScriptingObject, T>::Val
|
||||
MCore::GC::WriteArrayRef(result, Span<MObject*>(objects, data.Length()));
|
||||
Allocator::Free(objects);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for ScriptingObject References.
|
||||
@@ -284,16 +553,29 @@ struct MConverter<ScriptingObjectReference<T>>
|
||||
{
|
||||
MObject* Box(const ScriptingObjectReference<T>& data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
return data.GetManagedInstance();
|
||||
}
|
||||
|
||||
void Unbox(ScriptingObjectReference<T>& result, MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = (T*)ScriptingObject::ToNative(data);
|
||||
}
|
||||
|
||||
void ToManaged(MObject*& result, const ScriptingObjectReference<T>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(ScriptingObjectReference<T>& result, const MObject*& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<ScriptingObjectReference<T>>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
if (data.Length() == 0)
|
||||
return;
|
||||
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
|
||||
@@ -305,10 +587,23 @@ struct MConverter<ScriptingObjectReference<T>>
|
||||
|
||||
void ToNativeArray(Span<ScriptingObjectReference<T>>& result, const MArray* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Asset References.
|
||||
@@ -320,10 +615,23 @@ struct MConverter<AssetReference<T>>
|
||||
{
|
||||
MObject* Box(const AssetReference<T>& data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
return data.GetManagedInstance();
|
||||
}
|
||||
|
||||
void Unbox(AssetReference<T>& result, MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = (T*)ScriptingObject::ToNative(data);
|
||||
}
|
||||
|
||||
void ToManaged(MObject*& result, const AssetReference<T>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
result = data.GetManagedInstance();
|
||||
}
|
||||
|
||||
void ToNative(AssetReference<T>& result, const MObject* data)
|
||||
{
|
||||
result = (T*)ScriptingObject::ToNative(data);
|
||||
}
|
||||
@@ -341,10 +649,22 @@ struct MConverter<AssetReference<T>>
|
||||
|
||||
void ToNativeArray(Span<AssetReference<T>>& result, const MArray* data)
|
||||
{
|
||||
//PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: use MarshalAs=Guid on SoftAssetReference to pass guid over bindings and not load asset in glue code
|
||||
@@ -353,8 +673,32 @@ class SoftAssetReference;
|
||||
template<typename T>
|
||||
struct MConverter<SoftAssetReference<T>>
|
||||
{
|
||||
|
||||
template <typename U>
|
||||
void ToManaged(U& result, const SoftAssetReference<T>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void ToNative(SoftAssetReference<T>& result, const U& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManaged(MObject*& result, const SoftAssetReference<T>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(SoftAssetReference<T>& result, const MObject*& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToManagedArray(MArray* result, const Span<SoftAssetReference<T>>& data)
|
||||
{
|
||||
//PLATFORM_DEBUG_BREAK; // FIXME
|
||||
if (data.Length() == 0)
|
||||
return;
|
||||
MObject** objects = (MObject**)Allocator::Allocate(data.Length() * sizeof(MObject*));
|
||||
@@ -366,18 +710,42 @@ struct MConverter<SoftAssetReference<T>>
|
||||
|
||||
void ToNativeArray(Span<SoftAssetReference<T>>& result, const MArray* data)
|
||||
{
|
||||
//PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MObject** dataPtr = MCore::Array::GetAddress<MObject*>(data);
|
||||
for (int32 i = 0; i < result.Length(); i++)
|
||||
result.Get()[i] = (T*)ScriptingObject::ToNative(dataPtr[i]);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
// Converter for Array.
|
||||
template<typename T>
|
||||
struct MConverter<Array<T>>
|
||||
{
|
||||
void ToManaged(Array<T>& result, const Array<T>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
void ToNative(Array<T>& result, const Array<T>& data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
}
|
||||
|
||||
MObject* Box(const Array<T>& data, const MClass* klass)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
if (!klass)
|
||||
return nullptr;
|
||||
MArray* result = MCore::Array::New(klass->GetElementClass(), data.Count());
|
||||
@@ -395,6 +763,23 @@ struct MConverter<Array<T>>
|
||||
Span<T> resultSpan(result.Get(), length);
|
||||
converter.ToNativeArray(resultSpan, array);
|
||||
}
|
||||
|
||||
void FreeManaged(MObject* data)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MArray* array = (MArray*)data;
|
||||
MConverter<T> converter;
|
||||
converter.FreeManagedArray(array);
|
||||
//MCore::Array::Free(array);
|
||||
//MCore::GCHandle::Free(*(MGCHandle*)&data);
|
||||
}
|
||||
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
PLATFORM_DEBUG_BREAK; // FIXME
|
||||
MCore::Array::Free(array);
|
||||
MCore::GCHandle::Free(*(MGCHandle*)&array);
|
||||
}
|
||||
};
|
||||
|
||||
namespace MUtils
|
||||
@@ -435,6 +820,7 @@ namespace MUtils
|
||||
/// <summary>
|
||||
/// Unboxes MObject to the native value of the given type.
|
||||
/// </summary>
|
||||
///
|
||||
template<class T>
|
||||
T Unbox(MObject* object)
|
||||
{
|
||||
@@ -444,6 +830,71 @@ namespace MUtils
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unboxes MObject to the native value of the given type.
|
||||
/// </summary>
|
||||
/// <param name="object">The object.</param>
|
||||
/// <param name="releaseHandle">True if boxed managed handle should be released.</param>
|
||||
template<class T>
|
||||
T Unbox(MObject* object, bool releaseHandle)
|
||||
{
|
||||
MConverter<T> converter;
|
||||
T result;
|
||||
converter.Unbox(result, object);
|
||||
if (releaseHandle)
|
||||
converter.FreeManaged(object);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unboxes MObject to the native value of the given type.
|
||||
/// </summary>
|
||||
///
|
||||
template<>
|
||||
inline bool Unbox(MObject* object)
|
||||
{
|
||||
MConverter<bool> converter;
|
||||
int32 result;
|
||||
converter.Unbox(*(bool*)&result, object);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unboxes MObject to the native value of the given type.
|
||||
/// </summary>
|
||||
/// <param name="object">The object.</param>
|
||||
/// <param name="releaseHandle">True if boxed managed handle should be released.</param>
|
||||
template<>
|
||||
inline bool Unbox(MObject* object, bool releaseHandle)
|
||||
{
|
||||
MConverter<bool> converter;
|
||||
int32 result;
|
||||
converter.Unbox(*(bool*)&result, object);
|
||||
if (releaseHandle)
|
||||
converter.FreeManaged(object);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the managed resources of a boxed object.
|
||||
/// </summary>
|
||||
template<class T>
|
||||
void FreeManaged(MObject* object)
|
||||
{
|
||||
MConverter<T> converter;
|
||||
converter.FreeManaged(object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the managed resources of a boxed object.
|
||||
/// </summary>
|
||||
template<class T>
|
||||
void FreeManagedArray(MArray* array)
|
||||
{
|
||||
MConverter<T> converter;
|
||||
converter.FreeManagedArray(array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Links managed array data to the unmanaged BytesContainer.
|
||||
/// </summary>
|
||||
@@ -580,6 +1031,287 @@ namespace MUtils
|
||||
}
|
||||
|
||||
#if USE_NETCORE
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE T* ToNativeArray(const Array<T>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
T* arr = (T*)MCore::GC::AllocateMemory(data.Count() * sizeof(T), true);
|
||||
Platform::MemoryCopy(arr, data.Get(), data.Count() * sizeof(T));
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new managed array of data and copies contents from given native array.
|
||||
/// </summary>
|
||||
/// <param name="data">The array object.</param>
|
||||
/// <param name="valueClass">The array values type class.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
/*template<typename T, typename U>
|
||||
NativeArray<U> ToManagedArrayWrapper(const Span<T>& data, const MClass* valueClass)
|
||||
{
|
||||
if (!valueClass)
|
||||
return nullptr;
|
||||
MArray* result = MCore::Array::New(valueClass, data.Length());
|
||||
MConverter<T> converter;
|
||||
converter.ToManagedArray(result, data);
|
||||
return result;
|
||||
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<U> arr;
|
||||
arr.length = data.Count();
|
||||
arr.data = (U*)MCore::GC::AllocateMemory(arr.length * sizeof(U), true);
|
||||
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(U));
|
||||
return arr;
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
/*template<typename T, typename AllocationType = HeapAllocation>
|
||||
FORCE_INLINE NativeArray<T> ToNativeArrayWrapper(const Array<T, AllocationType>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<T> arr;
|
||||
arr.length = data.Count();
|
||||
arr.data = (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true);
|
||||
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T));
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T, typename U, typename AllocationType = HeapAllocation>
|
||||
FORCE_INLINE NativeArray<U> ToNativeArrayWrapper(const Array<T, AllocationType>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<U> arr;
|
||||
arr.length = data.Count();
|
||||
arr.data = (U*)MCore::GC::AllocateMemory(arr.length * sizeof(U), true);
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T, U> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
arr.data[i] = converter.ToManaged(data[i]);
|
||||
return arr;
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T, typename U>
|
||||
FORCE_INLINE NativeArray<U> ToManagedArrayWrapperConvert(const Span<T>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<U> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (U*)MCore::GC::AllocateMemory(arr.length * sizeof(U), true) : nullptr;
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T, U> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
converter.ToManaged(arr.data[i], data[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE NativeArray<T> ToNativeArrayWrapperConvertGeneric(const Span<MObject*>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<T> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr;
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
{
|
||||
converter.ToNative(arr.data[i], data[i]);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T, typename AllocationType = HeapAllocation>
|
||||
FORCE_INLINE void ToNativeArrayWrapperConvertGeneric(const Span<MObject*>& data, Array<T, AllocationType>& array)
|
||||
{
|
||||
if (!array.IsEmpty())
|
||||
PLATFORM_DEBUG_BREAK; // TODO: does the array require ClearDelete instead?
|
||||
|
||||
auto length = data.Length();
|
||||
array.Resize(length, false);
|
||||
//array.Clear();
|
||||
//array.EnsureCapacity(length);
|
||||
|
||||
MConverter<T> converter;
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
// Make sure the destination is default initialized first
|
||||
//array.AddUninitialized();
|
||||
//array.AddDefault();
|
||||
//T& dest = array.Get()[i];
|
||||
//new(&dest)AssetReference<T>();
|
||||
|
||||
// Convert from managed
|
||||
converter.ToNative(array.Get()[i], data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T, typename U>
|
||||
FORCE_INLINE NativeArray<T> ToNativeArrayWrapperConvert(const Span<U>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<T> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr;
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T, U> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
converter.ToNative(arr.data[i], data[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE NativeArray<T> ToNativeArrayWrapperCopy(const Span<T>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<T> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr;
|
||||
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T));
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
/*FORCE_INLINE NativeArray<Char> ToManagedArrayWrapper(const Span<Char>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<Char> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = (Char*)MCore::GC::AllocateMemory(arr.length * sizeof(Char), true);
|
||||
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(Char));
|
||||
return arr;
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE NativeArray<T> ToManagedArrayWrapperConvert(const Span<T>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<T> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr;
|
||||
//Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T));
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
converter.ToManaged(arr.data[i], data[i]);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<typename T>
|
||||
FORCE_INLINE NativeArray<T> ToManagedArrayWrapperCopy(const Span<T>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<T> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (T*)MCore::GC::AllocateMemory(arr.length * sizeof(T), true) : nullptr;
|
||||
Platform::MemoryCopy(arr.data, data.Get(), arr.length * sizeof(T));
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
/*template<typename T>
|
||||
FORCE_INLINE NativeArray<MObject*> ToManagedArrayWrapperPointer(const Span<T*>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<MObject*> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = (MObject**)MCore::GC::AllocateMemory(arr.length * sizeof(MObject*), true);
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T*> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
{
|
||||
MObject*& ptr = arr.data[i];
|
||||
const T& asd = data[i];
|
||||
converter.ToManaged(ptr, asd);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
/// <param name="data">The input data.</param>
|
||||
/// <returns>The output array.</returns>
|
||||
template<class T>
|
||||
FORCE_INLINE NativeArray<MObject*> ToManagedArrayWrapperPointer(const Span<T>& data)
|
||||
{
|
||||
// System.Runtime.InteropServices.Marshalling.ArrayMarshaller uses CoTask memory alloc to native data pointer
|
||||
NativeArray<MObject*> arr;
|
||||
arr.length = data.Length();
|
||||
arr.data = arr.length > 0 ? (MObject**)MCore::GC::AllocateMemory(arr.length * sizeof(MObject*), true) : nullptr;
|
||||
|
||||
// Convert to managed
|
||||
MConverter<T> converter;
|
||||
for (int i = 0; i < arr.length; ++i)
|
||||
converter.ToManaged(arr.data[i], data[i]);
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates new boolean array and copies data from the given unmanaged data container. The managed runtime is responsible for releasing the returned array data.
|
||||
/// </summary>
|
||||
|
||||
@@ -399,10 +399,15 @@ MObject* MCore::Object::Box(void* value, const MClass* klass)
|
||||
return (MObject*)CallStaticMethod<void*, void*, void*>(BoxValuePtr, klass->_handle, value);
|
||||
}
|
||||
|
||||
void* MCore::Object::Unbox(MObject* obj)
|
||||
void* MCore::Object::Unbox(const MObject* obj)
|
||||
{
|
||||
CRASH; // Should not be used anymore
|
||||
}
|
||||
|
||||
void MCore::Object::Unbox(const MObject* obj, void* dest)
|
||||
{
|
||||
static void* UnboxValuePtr = GetStaticMethodPointer(TEXT("UnboxValue"));
|
||||
return CallStaticMethod<void*, void*>(UnboxValuePtr, obj);
|
||||
return CallStaticMethod<void, const void*, void*>(UnboxValuePtr, obj, dest);
|
||||
}
|
||||
|
||||
MObject* MCore::Object::New(const MClass* klass)
|
||||
@@ -411,64 +416,76 @@ MObject* MCore::Object::New(const MClass* klass)
|
||||
return (MObject*)CallStaticMethod<void*, void*>(NewObjectPtr, klass->_handle);
|
||||
}
|
||||
|
||||
void MCore::Object::Init(MObject* obj)
|
||||
void MCore::Object::Init(const MObject* obj)
|
||||
{
|
||||
static void* ObjectInitPtr = GetStaticMethodPointer(TEXT("ObjectInit"));
|
||||
CallStaticMethod<void, void*>(ObjectInitPtr, obj);
|
||||
CallStaticMethod<void, const void*>(ObjectInitPtr, obj);
|
||||
}
|
||||
|
||||
MClass* MCore::Object::GetClass(MObject* obj)
|
||||
MClass* MCore::Object::GetClass(const MObject* obj)
|
||||
{
|
||||
ASSERT(obj);
|
||||
static void* GetObjectClassPtr = GetStaticMethodPointer(TEXT("GetObjectClass"));
|
||||
return (MClass*)CallStaticMethod<MClass*, void*>(GetObjectClassPtr, obj);
|
||||
return (MClass*)CallStaticMethod<MClass*, const void*>(GetObjectClassPtr, obj);
|
||||
}
|
||||
|
||||
MString* MCore::Object::ToString(MObject* obj)
|
||||
MString* MCore::Object::ToString(const MObject* obj)
|
||||
{
|
||||
static void* GetObjectStringPtr = GetStaticMethodPointer(TEXT("GetObjectString"));
|
||||
return (MString*)CallStaticMethod<void*, void*>(GetObjectStringPtr, obj);
|
||||
return (MString*)CallStaticMethod<void*, const void*>(GetObjectStringPtr, obj);
|
||||
}
|
||||
|
||||
int32 MCore::Object::GetHashCode(MObject* obj)
|
||||
int32 MCore::Object::GetHashCode(const MObject* obj)
|
||||
{
|
||||
static void* GetObjectStringPtr = GetStaticMethodPointer(TEXT("GetObjectHashCode"));
|
||||
return CallStaticMethod<int32, void*>(GetObjectStringPtr, obj);
|
||||
return CallStaticMethod<int32, const void*>(GetObjectStringPtr, obj);
|
||||
}
|
||||
|
||||
MString* MCore::String::GetEmpty(MDomain* domain)
|
||||
MString* MCore::String::GetEmpty(const MDomain* domain)
|
||||
{
|
||||
static void* GetStringEmptyPtr = GetStaticMethodPointer(TEXT("GetStringEmpty"));
|
||||
return (MString*)CallStaticMethod<void*>(GetStringEmptyPtr);
|
||||
}
|
||||
|
||||
MString* MCore::String::New(const char* str, int32 length, MDomain* domain)
|
||||
MString* MCore::String::New(const char* str, int32 length, const MDomain* domain)
|
||||
{
|
||||
static void* NewStringUTF8Ptr = GetStaticMethodPointer(TEXT("NewStringUTF8"));
|
||||
return (MString*)CallStaticMethod<void*, const char*, int>(NewStringUTF8Ptr, str, length);
|
||||
}
|
||||
|
||||
MString* MCore::String::New(const Char* str, int32 length, MDomain* domain)
|
||||
MString* MCore::String::New(const Char* str, int32 length, const MDomain* domain)
|
||||
{
|
||||
static void* NewStringUTF16Ptr = GetStaticMethodPointer(TEXT("NewStringUTF16"));
|
||||
return (MString*)CallStaticMethod<void*, const Char*, int>(NewStringUTF16Ptr, str, length);
|
||||
}
|
||||
|
||||
StringView MCore::String::GetChars(MString* obj)
|
||||
StringView MCore::String::GetChars(const MString* obj)
|
||||
{
|
||||
int32 length = 0;
|
||||
static void* GetStringPointerPtr = GetStaticMethodPointer(TEXT("GetStringPointer"));
|
||||
const Char* chars = CallStaticMethod<const Char*, void*, int*>(GetStringPointerPtr, obj, &length);
|
||||
const Char* chars = CallStaticMethod<const Char*, const void*, int*>(GetStringPointerPtr, obj, &length);
|
||||
return StringView(chars, length);
|
||||
}
|
||||
|
||||
void MCore::String::Free(const MString* obj)
|
||||
{
|
||||
static void* FreeStringPtr = GetStaticMethodPointer(TEXT("FreeString"));
|
||||
CallStaticMethod<void, const void*>(FreeStringPtr, obj);
|
||||
}
|
||||
|
||||
MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
|
||||
{
|
||||
static void* NewArrayPtr = GetStaticMethodPointer(TEXT("NewArray"));
|
||||
return (MArray*)CallStaticMethod<void*, void*, long long>(NewArrayPtr, elementKlass->_handle, length);
|
||||
}
|
||||
|
||||
MClass* MCore::Array::GetClass(MClass* elementKlass)
|
||||
void MCore::Array::Free(const MArray* array)
|
||||
{
|
||||
static void* FreeArrayPtr = GetStaticMethodPointer(TEXT("FreeArray"));
|
||||
CallStaticMethod<void, void*>(FreeArrayPtr, (void*)array);
|
||||
}
|
||||
|
||||
MClass* MCore::Array::GetClass(const MClass* elementKlass)
|
||||
{
|
||||
static void* GetArrayTypeFromElementTypePtr = GetStaticMethodPointer(TEXT("GetArrayTypeFromElementType"));
|
||||
MType* typeHandle = (MType*)CallStaticMethod<void*, void*>(GetArrayTypeFromElementTypePtr, elementKlass->_handle);
|
||||
@@ -494,24 +511,24 @@ void* MCore::Array::GetAddress(const MArray* obj)
|
||||
return CallStaticMethod<void*, void*>(GetArrayPointerPtr, (void*)obj);
|
||||
}
|
||||
|
||||
MArray* MCore::Array::Unbox(MObject* obj)
|
||||
MArray* MCore::Array::Unbox(const MObject* obj)
|
||||
{
|
||||
static void* GetArrayPtr = GetStaticMethodPointer(TEXT("GetArray"));
|
||||
return (MArray*)CallStaticMethod<void*, void*>(GetArrayPtr, (void*)obj);
|
||||
}
|
||||
|
||||
MGCHandle MCore::GCHandle::New(MObject* obj, bool pinned)
|
||||
MGCHandle MCore::GCHandle::New(const MObject* obj, bool pinned)
|
||||
{
|
||||
ASSERT(obj);
|
||||
static void* NewGCHandlePtr = GetStaticMethodPointer(TEXT("NewGCHandle"));
|
||||
return (MGCHandle)CallStaticMethod<void*, void*, bool>(NewGCHandlePtr, obj, pinned);
|
||||
return (MGCHandle)CallStaticMethod<void*, const void*, bool>(NewGCHandlePtr, obj, pinned);
|
||||
}
|
||||
|
||||
MGCHandle MCore::GCHandle::NewWeak(MObject* obj, bool trackResurrection)
|
||||
MGCHandle MCore::GCHandle::NewWeak(const MObject* obj, bool trackResurrection)
|
||||
{
|
||||
ASSERT(obj);
|
||||
static void* NewGCHandleWeakPtr = GetStaticMethodPointer(TEXT("NewGCHandleWeak"));
|
||||
return (MGCHandle)CallStaticMethod<void*, void*, bool>(NewGCHandleWeakPtr, obj, trackResurrection);
|
||||
return (MGCHandle)CallStaticMethod<void*, const void*, bool>(NewGCHandleWeakPtr, obj, trackResurrection);
|
||||
}
|
||||
|
||||
MObject* MCore::GCHandle::GetTarget(const MGCHandle& handle)
|
||||
@@ -883,7 +900,6 @@ bool MAssembly::LoadCorlib()
|
||||
|
||||
bool MAssembly::LoadImage(const String& assemblyPath, const StringView& nativePath)
|
||||
{
|
||||
// TODO: Use new hostfxr delegate load_assembly_bytes? (.NET 8+)
|
||||
// Open .Net assembly
|
||||
static void* LoadAssemblyImagePtr = GetStaticMethodPointer(TEXT("LoadAssemblyImage"));
|
||||
_handle = CallStaticMethod<void*, const Char*>(LoadAssemblyImagePtr, assemblyPath.Get());
|
||||
@@ -1371,28 +1387,28 @@ int32 MField::GetOffset() const
|
||||
return _fieldOffset;
|
||||
}
|
||||
|
||||
void MField::GetValue(MObject* instance, void* result) const
|
||||
void MField::GetValue(const MObject* instance, void* result) const
|
||||
{
|
||||
static void* FieldGetValuePtr = GetStaticMethodPointer(TEXT("FieldGetValue"));
|
||||
CallStaticMethod<void, void*, void*, void*>(FieldGetValuePtr, instance, _handle, result);
|
||||
CallStaticMethod<void, const void*, void*, void*>(FieldGetValuePtr, instance, _handle, result);
|
||||
}
|
||||
|
||||
void MField::GetValueReference(MObject* instance, void* result) const
|
||||
void MField::GetValueReference(const MObject* instance, void* result) const
|
||||
{
|
||||
static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReference"));
|
||||
CallStaticMethod<void, void*, void*, int, void*>(FieldGetValueReferencePtr, instance, _handle, _fieldOffset, result);
|
||||
CallStaticMethod<void, const void*, void*, int, void*>(FieldGetValueReferencePtr, instance, _handle, _fieldOffset, result);
|
||||
}
|
||||
|
||||
MObject* MField::GetValueBoxed(MObject* instance) const
|
||||
MObject* MField::GetValueBoxed(const MObject* instance) const
|
||||
{
|
||||
static void* FieldGetValueBoxedPtr = GetStaticMethodPointer(TEXT("FieldGetValueBoxed"));
|
||||
return CallStaticMethod<MObject*, void*, void*>(FieldGetValueBoxedPtr, instance, _handle);
|
||||
return CallStaticMethod<MObject*, const void*, void*>(FieldGetValueBoxedPtr, instance, _handle);
|
||||
}
|
||||
|
||||
void MField::SetValue(MObject* instance, void* value) const
|
||||
void MField::SetValue(const MObject* instance, void* value) const
|
||||
{
|
||||
static void* FieldSetValuePtr = GetStaticMethodPointer(TEXT("FieldSetValue"));
|
||||
CallStaticMethod<void, void*, void*, void*>(FieldSetValuePtr, instance, _handle, value);
|
||||
CallStaticMethod<void, const void*, void*, void*>(FieldSetValuePtr, instance, _handle, value);
|
||||
}
|
||||
|
||||
bool MField::HasAttribute(const MClass* klass) const
|
||||
|
||||
@@ -738,6 +738,11 @@ void* MCore::Object::Unbox(MObject* obj)
|
||||
//return mono_object_unbox(obj);
|
||||
}
|
||||
|
||||
void MCore::Object::Unbox(MObject* obj, void* dest)
|
||||
{
|
||||
CRASH; // Not applicable
|
||||
}
|
||||
|
||||
MObject* MCore::Object::New(const MClass* klass)
|
||||
{
|
||||
return mono_object_new(mono_domain_get(), klass->GetNative());
|
||||
@@ -787,12 +792,20 @@ StringView MCore::String::GetChars(MString* obj)
|
||||
return StringView(mono_string_chars(obj), (int32)mono_string_length(obj));
|
||||
}
|
||||
|
||||
void MCore::String::Free(MString* obj)
|
||||
{
|
||||
}
|
||||
|
||||
MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
|
||||
{
|
||||
// TODO: use shared empty arrays cache
|
||||
return mono_array_new(mono_domain_get(), elementKlass->GetNative(), length);
|
||||
}
|
||||
|
||||
void MCore::Array::Free(const MArray* array)
|
||||
{
|
||||
}
|
||||
|
||||
MClass* MCore::Array::GetClass(MClass* elementKlass)
|
||||
{
|
||||
MonoClass* monoClass = mono_array_class_get(elementKlass->GetNative(), 1);
|
||||
|
||||
@@ -80,6 +80,10 @@ void* MCore::Object::Unbox(MObject* obj)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MCore::Object::Unbox(MObject* obj, void* dest)
|
||||
{
|
||||
}
|
||||
|
||||
MObject* MCore::Object::New(const MClass* klass)
|
||||
{
|
||||
return nullptr;
|
||||
@@ -124,11 +128,19 @@ StringView MCore::String::GetChars(MString* obj)
|
||||
return StringView::Empty;
|
||||
}
|
||||
|
||||
void MCore::String::Free(MString* obj)
|
||||
{
|
||||
}
|
||||
|
||||
MArray* MCore::Array::New(const MClass* elementKlass, int32 length)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MCore::Array::Free(const MArray* array)
|
||||
{
|
||||
}
|
||||
|
||||
MClass* MCore::Array::GetClass(MClass* elementKlass)
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -245,7 +245,7 @@ void* ScriptingObject::ToInterface(ScriptingObject* obj, const ScriptingTypeHand
|
||||
return result;
|
||||
}
|
||||
|
||||
ScriptingObject* ScriptingObject::ToNative(MObject* obj)
|
||||
ScriptingObject* ScriptingObject::ToNative(const MObject* obj)
|
||||
{
|
||||
ScriptingObject* ptr = nullptr;
|
||||
#if USE_CSHARP
|
||||
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
return (T*)ToInterface(obj, T::TypeInitializer);
|
||||
}
|
||||
|
||||
static ScriptingObject* ToNative(MObject* obj);
|
||||
static ScriptingObject* ToNative(const MObject* obj);
|
||||
|
||||
FORCE_INLINE static MObject* ToManaged(const ScriptingObject* obj)
|
||||
{
|
||||
|
||||
82
Source/Engine/Tests/TestCommandLine.cpp
Normal file
82
Source/Engine/Tests/TestCommandLine.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include <ThirdParty/catch2/catch.hpp>
|
||||
|
||||
TEST_CASE("CommandLine")
|
||||
{
|
||||
SECTION("Test Argument Parser")
|
||||
{
|
||||
SECTION("Single quoted word")
|
||||
{
|
||||
String input("\"word\"");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 1);
|
||||
CHECK(arguments[0].Compare(StringAnsi("word")) == 0);
|
||||
}
|
||||
SECTION("Quotes at the beginning of the word")
|
||||
{
|
||||
String input("start\"word\"");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 1);
|
||||
CHECK(arguments[0].Compare(StringAnsi("start\"word\"")) == 0);
|
||||
}
|
||||
SECTION("Quotes in the middle of the word")
|
||||
{
|
||||
String input("start\"word\"end");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 1);
|
||||
CHECK(arguments[0].Compare(StringAnsi("start\"word\"end")) == 0);
|
||||
}
|
||||
SECTION("Quotes at the end of the word")
|
||||
{
|
||||
String input("\"word\"end");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 1);
|
||||
CHECK(arguments[0].Compare(StringAnsi("\"word\"end")) == 0);
|
||||
}
|
||||
SECTION("Multiple words")
|
||||
{
|
||||
String input("The quick brown fox");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 4);
|
||||
CHECK(arguments[0].Compare(StringAnsi("The")) == 0);
|
||||
CHECK(arguments[1].Compare(StringAnsi("quick")) == 0);
|
||||
CHECK(arguments[2].Compare(StringAnsi("brown")) == 0);
|
||||
CHECK(arguments[3].Compare(StringAnsi("fox")) == 0);
|
||||
}
|
||||
SECTION("Multiple words with quotes")
|
||||
{
|
||||
String input("The \"quick brown fox\" jumps over the \"lazy\" dog");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 7);
|
||||
CHECK(arguments[0].Compare(StringAnsi("The")) == 0);
|
||||
CHECK(arguments[1].Compare(StringAnsi("quick brown fox")) == 0);
|
||||
CHECK(arguments[2].Compare(StringAnsi("jumps")) == 0);
|
||||
CHECK(arguments[3].Compare(StringAnsi("over")) == 0);
|
||||
CHECK(arguments[4].Compare(StringAnsi("the")) == 0);
|
||||
CHECK(arguments[5].Compare(StringAnsi("lazy")) == 0);
|
||||
CHECK(arguments[6].Compare(StringAnsi("dog")) == 0);
|
||||
}
|
||||
SECTION("Flax.Build sample parameters")
|
||||
{
|
||||
String input("-log -mutex -workspace=\"C:\\path with spaces/to/FlaxEngine/\" -configuration=Debug -hotreload=\".HotReload.1\"");
|
||||
Array<StringAnsi> arguments;
|
||||
CHECK(!CommandLine::ParseArguments(input, arguments));
|
||||
CHECK(arguments.Count() == 5);
|
||||
CHECK(arguments[0].Compare(StringAnsi("-log")) == 0);
|
||||
CHECK(arguments[1].Compare(StringAnsi("-mutex")) == 0);
|
||||
CHECK(arguments[2].Compare(StringAnsi("-workspace=\"C:\\path with spaces/to/FlaxEngine/\"")) == 0);
|
||||
CHECK(arguments[3].Compare(StringAnsi("-configuration=Debug")) == 0);
|
||||
CHECK(arguments[4].Compare(StringAnsi("-hotreload=\".HotReload.1\"")) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ TEST_CASE("Scripting")
|
||||
CHECK(method);
|
||||
MObject* result = method->Invoke(nullptr, nullptr, nullptr);
|
||||
CHECK(result);
|
||||
int32 resultValue = MUtils::Unbox<int32>(result);
|
||||
int32 resultValue = MUtils::Unbox<int32>(result, true);
|
||||
CHECK(resultValue == 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -745,7 +745,7 @@ void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
|
||||
StringAsANSI<40> fieldNameAnsi(*fieldName, fieldName.Length());
|
||||
auto field = mclass->GetField(fieldNameAnsi.Get());
|
||||
if (field)
|
||||
value = MUtils::UnboxVariant(field->GetValueBoxed(instance));
|
||||
value = MUtils::UnboxVariant(field->GetValueBoxed(instance), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,12 +633,86 @@ namespace Flax.Build.Bindings
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
|
||||
else if (returnValueType == "object[]")
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
|
||||
else if (functionInfo.ReturnType.IsArrayOrSpan || returnNativeType == "Array")
|
||||
{
|
||||
var elementApiType = functionInfo.ReturnType.GenericArgs?.Count > 0 ? FindApiTypeInfo(buildData, functionInfo.ReturnType.GenericArgs[0], caller) : null;
|
||||
string unmanagedType = "";
|
||||
//if (functionInfo.ReturnType.GenericArgs != null && functionInfo.ReturnType.GenericArgs[0].IsPod(buildData, caller))
|
||||
var genericType = functionInfo.ReturnType.GenericArgs?[0].Type;
|
||||
if (functionInfo.ReturnType.Type == "BytesContainer")
|
||||
genericType = "byte";
|
||||
if (functionInfo.ReturnType.IsObjectRef || (elementApiType?.IsScriptingObject ?? false))
|
||||
genericType = null;
|
||||
|
||||
switch (genericType)
|
||||
{
|
||||
case "bool": unmanagedType = "U1"; break;
|
||||
case "byte": unmanagedType = "U1"; break;
|
||||
case "sbyte": unmanagedType = "I1"; break;
|
||||
case "char": unmanagedType = "I2"; break;
|
||||
case "short": case "int16": unmanagedType = "I2"; break;
|
||||
case "ushort": case "uint16": unmanagedType = "U2"; break;
|
||||
case "int": case "int32": unmanagedType = "I4"; break;
|
||||
case "uint": case "uint32": unmanagedType = "U4"; break;
|
||||
case "long": case "int64": unmanagedType = "I8"; break;
|
||||
case "ulong": case "uint64": unmanagedType = "U8"; break;
|
||||
case "float": unmanagedType = "R4"; break;
|
||||
case "double": unmanagedType = "R8"; break;
|
||||
|
||||
case "Float2":
|
||||
case "Double2":
|
||||
case "Vector2":
|
||||
case "Float3":
|
||||
case "Double3":
|
||||
case "Vector3":
|
||||
case "Float4":
|
||||
case "Double4":
|
||||
case "Vector4":
|
||||
case "Color":
|
||||
case "Color32":
|
||||
case "Guid":
|
||||
unmanagedType = "Any"; // FIXME
|
||||
break;
|
||||
|
||||
case null:
|
||||
case "":
|
||||
case "String":
|
||||
case "StringAnsi":
|
||||
case "StringView":
|
||||
unmanagedType = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (elementApiType != null && (elementApiType.IsClass || !elementApiType.IsPod))
|
||||
unmanagedType = null;
|
||||
else
|
||||
unmanagedType = "Any";
|
||||
//Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'");
|
||||
break;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(unmanagedType))
|
||||
{
|
||||
string arraySubType = "";
|
||||
if (unmanagedType != "Any")
|
||||
arraySubType = $"ArraySubType = UnmanagedType.{unmanagedType}, ";
|
||||
returnMarshalType = $"MarshalAs(UnmanagedType.LPArray, {arraySubType}SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__returnCount"))})";
|
||||
}
|
||||
else if (genericType == "String" || genericType == "StringAnsi" || genericType == "StringView")
|
||||
returnMarshalType = $"/*marsh2c*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller<string, IntPtr>), CountElementName = \"__returnCount\")";
|
||||
else if (elementApiType?.IsValueType ?? true)
|
||||
returnMarshalType = $"/*marsh2a*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller<,>), CountElementName = \"__returnCount\")";
|
||||
else
|
||||
returnMarshalType = $"/*marsh2b*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__returnCount\")";
|
||||
}
|
||||
else if (functionInfo.ReturnType.Type == "Array" || functionInfo.ReturnType.Type == "Span" || functionInfo.ReturnType.Type == "DataContainer" || functionInfo.ReturnType.Type == "BytesContainer" || returnNativeType == "Array")
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
|
||||
{
|
||||
//[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 8)]
|
||||
returnMarshalType = "/*marsh3*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = nameof(__returnCount))";
|
||||
}
|
||||
else if (functionInfo.ReturnType.Type == "Dictionary")
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
||||
else if (returnValueType == "byte[]")
|
||||
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__returnCount\")";
|
||||
returnMarshalType = "/*marsh4*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__returnCount\")";
|
||||
else if (returnValueType == "bool[]")
|
||||
{
|
||||
// Boolean arrays does not support custom marshalling for some unknown reason
|
||||
@@ -686,13 +760,97 @@ namespace Flax.Build.Bindings
|
||||
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemArrayMarshaller))";
|
||||
else if (nativeType == "object[]")
|
||||
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.SystemObjectArrayMarshaller))";
|
||||
else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool")
|
||||
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})";
|
||||
else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array")
|
||||
else if (parameterInfo.Type.IsArrayOrSpan || nativeType == "Array")
|
||||
{
|
||||
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
|
||||
var elementApiType = parameterInfo.Type.GenericArgs?.Count > 0 ? FindApiTypeInfo(buildData, parameterInfo.Type.GenericArgs[0], caller) : null;
|
||||
string unmanagedType = "";
|
||||
var genericType = parameterInfo.Type.GenericArgs?[0].Type;
|
||||
if (parameterInfo.Type.Type == "BytesContainer")
|
||||
genericType = "byte";
|
||||
if (parameterInfo.Type.IsObjectRef || (elementApiType?.IsScriptingObject ?? false))
|
||||
genericType = null;
|
||||
|
||||
switch (genericType)
|
||||
{
|
||||
case "bool": unmanagedType = "U1"; break;
|
||||
case "byte": unmanagedType = "U1"; break;
|
||||
case "sbyte": unmanagedType = "I1"; break;
|
||||
case "char": unmanagedType = "I2"; break;
|
||||
case "short": case "int16": unmanagedType = "I2"; break;
|
||||
case "ushort": case "uint16": unmanagedType = "U2"; break;
|
||||
case "int": case "int32": unmanagedType = "I4"; break;
|
||||
case "uint": case "uint32": unmanagedType = "U4"; break;
|
||||
case "long": case "int64": unmanagedType = "I8"; break;
|
||||
case "ulong": case "uint64": unmanagedType = "U8"; break;
|
||||
case "float": unmanagedType = "R4"; break;
|
||||
case "double": unmanagedType = "R8"; break;
|
||||
|
||||
case "Float2":
|
||||
case "Double2":
|
||||
case "Vector2":
|
||||
case "Float3":
|
||||
case "Double3":
|
||||
case "Vector3":
|
||||
case "Float4":
|
||||
case "Double4":
|
||||
case "Vector4":
|
||||
case "Color":
|
||||
case "Color32":
|
||||
case "Guid":
|
||||
unmanagedType = "Any"; // FIXME
|
||||
break;
|
||||
|
||||
case null:
|
||||
case "":
|
||||
case "String":
|
||||
case "StringAnsi":
|
||||
case "StringView":
|
||||
unmanagedType = null;
|
||||
break;
|
||||
|
||||
case "AntiRollBar":
|
||||
unmanagedType = "Any"; // FIXME: This looks like a POD-type but isn't one?
|
||||
break;
|
||||
|
||||
default:
|
||||
if (elementApiType != null && (elementApiType.IsClass || !elementApiType.IsPod))
|
||||
unmanagedType = null;
|
||||
else if (elementApiType == null)
|
||||
unmanagedType = "Any";
|
||||
else
|
||||
unmanagedType = "Any";
|
||||
//Log.Warning($"unknown type: '{parameterInfo.Type.GenericArgs[0].Type}'");
|
||||
break;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(unmanagedType))
|
||||
{
|
||||
string arraySubType = "";
|
||||
if (unmanagedType != "Any")
|
||||
arraySubType = $"ArraySubType = UnmanagedType.{unmanagedType}, ";
|
||||
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, {arraySubType}SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == $"__{parameterInfo.Name}Count"))})";
|
||||
}
|
||||
else if (genericType == "String" || genericType == "StringAnsi" || genericType == "StringView")
|
||||
parameterMarshalType = $"/*marsh5c*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<string, IntPtr>), CountElementName = \"__{parameterInfo.Name}Count\")";
|
||||
else if (elementApiType.IsValueType)
|
||||
{
|
||||
var nativeName = parameterInfo.Type.GetFullNameNative(buildData, caller);
|
||||
var nativeInternalName = GenerateCppManagedWrapperName(elementApiType);
|
||||
var marshallerName = (!string.IsNullOrEmpty(elementApiType?.Namespace) ? $"{elementApiType?.Namespace}.Interop." : "") + $"{elementApiType.Name}Marshaller";
|
||||
//parameterMarshalType = $"/*marsh5a pod:{elementApiType.IsPod}*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
|
||||
if (/*nativeName != nativeInternalName && */elementApiType.MarshalAs != null)
|
||||
parameterMarshalType = $"/*marsh5a1 pod:{elementApiType.IsPod}*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
|
||||
else
|
||||
parameterMarshalType = $"/*marsh5a2 pod:{elementApiType.IsPod}*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<{elementApiType.Name},{marshallerName}.{elementApiType.Name}Internal>), CountElementName = \"__{parameterInfo.Name}Count\")";
|
||||
//parameterMarshalType += $"] [MarshalUsing(typeof({marshallerName}), ElementIndirectionDepth = 1)";
|
||||
// [MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
|
||||
}
|
||||
else
|
||||
parameterMarshalType = $"/*marsh5b*/MarshalUsing(typeof(FlaxEngine.Interop.BoxedArrayMarshaller<,>), CountElementName = \"__{parameterInfo.Name}Count\")";
|
||||
if (!parameterInfo.IsOut && !parameterInfo.IsRef)
|
||||
parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters.
|
||||
|
||||
//if (elementApiType?.MarshalAs != null)
|
||||
// parameterMarshalType += $"] [MarshalUsing(typeof({elementApiType.MarshalAs}), ElementIndirectionDepth = 1)";
|
||||
}
|
||||
else if (parameterInfo.Type.Type == "Dictionary")
|
||||
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
||||
@@ -702,7 +860,7 @@ namespace Flax.Build.Bindings
|
||||
parameterMarshalType = "MarshalAs(UnmanagedType.I2)";
|
||||
else if (nativeType.EndsWith("[]"))
|
||||
{
|
||||
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>))";
|
||||
parameterMarshalType = $"/*marsh6*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>))";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(parameterMarshalType))
|
||||
@@ -711,7 +869,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
|
||||
contents.Append("ref ");
|
||||
{
|
||||
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
|
||||
contents.Append("in ");
|
||||
else
|
||||
contents.Append("ref ");
|
||||
}
|
||||
|
||||
// Out parameters that need additional converting will be converted at the native side (eg. object reference)
|
||||
if (parameterInfo.IsOut && !string.IsNullOrEmpty(GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller)))
|
||||
@@ -736,8 +899,8 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var __resultAsRef")
|
||||
{
|
||||
// TODO: make this code shared with MarshalUsing selection from the above
|
||||
if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer")
|
||||
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
|
||||
if (parameterInfo.Type.IsArrayOrSpan)
|
||||
parameterMarshalType = $"/*marsh1*/MarshalUsing(typeof(FlaxEngine.Interop.NativeArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
|
||||
else if (parameterInfo.Type.Type == "Dictionary")
|
||||
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.Interop.DictionaryMarshaller<,>), ConstantElementCount = 0)";
|
||||
else if (parameterInfo.Type.Type == "CultureInfo")
|
||||
@@ -754,7 +917,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
|
||||
contents.Append("ref ");
|
||||
{
|
||||
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
|
||||
contents.Append("in ");
|
||||
else
|
||||
contents.Append("/*xcvg1*/ ref ");
|
||||
}
|
||||
contents.Append(nativeType);
|
||||
contents.Append(' ');
|
||||
contents.Append(parameterInfo.Name);
|
||||
@@ -802,7 +970,17 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
|
||||
contents.Append("ref ");
|
||||
{
|
||||
if (isSetter || parameterInfo.Type.IsConst)
|
||||
contents.Append("in ");
|
||||
else
|
||||
{
|
||||
//CanUpdate(BehaviorUpdateContext
|
||||
if (parameterInfo.Name == "context" && functionInfo.Name == "CanUpdate")
|
||||
separator = separator;
|
||||
contents.Append("/*xcvg2*/ ref ");
|
||||
}
|
||||
}
|
||||
|
||||
var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller);
|
||||
var paramName = isSetter ? "value" : parameterInfo.Name;
|
||||
@@ -836,7 +1014,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
|
||||
contents.Append("ref ");
|
||||
{
|
||||
if (parameterInfo.IsConst ||parameterInfo.Type.IsConst)
|
||||
contents.Append("in ");
|
||||
else
|
||||
contents.Append("ref ");
|
||||
}
|
||||
|
||||
// Pass value
|
||||
contents.Append(parameterInfo.DefaultValue);
|
||||
@@ -1369,7 +1552,12 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef)
|
||||
contents.Append("ref ");
|
||||
{
|
||||
if (parameterInfo.IsConst || parameterInfo.Type.IsConst)
|
||||
contents.Append("in ");
|
||||
else
|
||||
contents.Append("/*faffaf1*/ ref ");
|
||||
}
|
||||
else if (parameterInfo.IsThis)
|
||||
contents.Append("this ");
|
||||
else if (parameterInfo.IsParams)
|
||||
@@ -1431,7 +1619,7 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef)
|
||||
contents.Append("ref ");
|
||||
contents.Append("/*faffaf2*/ ref ");
|
||||
else if (parameterInfo.IsThis)
|
||||
contents.Append("this ");
|
||||
else if (parameterInfo.IsParams)
|
||||
@@ -1522,8 +1710,8 @@ namespace Flax.Build.Bindings
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => Unsafe.As<{{classInfo.Name}}>(ManagedHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged));
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed, GCHandleType.Weak) : IntPtr.Zero;
|
||||
public static void Free(IntPtr unmanaged) {}
|
||||
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => managed != null ? ManagedHandle.ToIntPtr(managed/*, GCHandleType.Weak*/) : IntPtr.Zero;
|
||||
public static void Free(IntPtr unmanaged) => ManagedHandleMarshaller.Free(unmanaged);
|
||||
}
|
||||
#if FLAX_EDITOR
|
||||
[HideInEditor]
|
||||
@@ -1677,11 +1865,22 @@ namespace Flax.Build.Bindings
|
||||
type = "IntPtr";
|
||||
else if (marshalType.IsPtr && !originalType.EndsWith("*"))
|
||||
type = "IntPtr";
|
||||
else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
|
||||
else if (marshalType.IsArrayOrSpan)
|
||||
{
|
||||
type = "IntPtr";
|
||||
apiType = FindApiTypeInfo(buildData, marshalType.GenericArgs[0], structureInfo);
|
||||
internalType = apiType is StructureInfo elementStructureInfo && UseCustomMarshalling(buildData, elementStructureInfo, structureInfo);
|
||||
string originalElementType = originalType.Substring(0, originalType.Length - 2);
|
||||
if (internalType)
|
||||
{
|
||||
string originalElementTypeMarshaller = (!string.IsNullOrEmpty(apiType?.Namespace) ? $"{apiType?.Namespace}.Interop." : "") + $"{originalElementType}Marshaller";
|
||||
string originalElementTypeName = originalElementType.Substring(originalElementType.LastIndexOf('.') + 1); // Strip namespace
|
||||
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementTypeName}Internal";
|
||||
type = $"NativeArray<{internalElementType}>";
|
||||
}
|
||||
else if (marshalType.GenericArgs[0].IsObjectRef)
|
||||
type = "NativeArray<IntPtr>";
|
||||
else
|
||||
type = $"NativeArray<{originalElementType}>";
|
||||
}
|
||||
else if (marshalType.Type == "Version")
|
||||
type = "IntPtr";
|
||||
@@ -1698,17 +1897,19 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
//else if (type == "Guid")
|
||||
// type = "GuidNative";
|
||||
structContents.Append($"{type} {fieldInfo.Name};").Append(type == "IntPtr" ? $" // {originalType}" : "").AppendLine();
|
||||
structContents.Append($"{type} {fieldInfo.Name};").Append(type != originalType ? $" // {originalType}" : "").AppendLine();
|
||||
}
|
||||
|
||||
// Generate struct constructor/getter and deconstructor/setter function
|
||||
toManagedContent.Append("managed.").Append(fieldInfo.Name).Append(" = ");
|
||||
toNativeContent.Append("unmanaged.").Append(fieldInfo.Name).Append(" = ");
|
||||
var managedField = $"managed.{fieldInfo.Name}";
|
||||
var unmanagedField = $"unmanaged.{fieldInfo.Name}";
|
||||
toManagedContent.Append($"{managedField} = ");
|
||||
toNativeContent.Append($"{unmanagedField} = ");
|
||||
if (marshalType.IsObjectRef)
|
||||
{
|
||||
var managedType = GenerateCSharpNativeToManaged(buildData, marshalType.GenericArgs[0], structureInfo);
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{managedType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 1");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1717,7 +1918,7 @@ namespace Flax.Build.Bindings
|
||||
else if (marshalType.Type == "ScriptingObject")
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<FlaxEngine.Object>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 2");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1726,7 +1927,7 @@ namespace Flax.Build.Bindings
|
||||
else if (marshalType.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak) : IntPtr.Zero;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name} != null ? ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/) : IntPtr.Zero; // plaa 3");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
@@ -1735,11 +1936,11 @@ namespace Flax.Build.Bindings
|
||||
else if (marshalType.Type == "Dictionary")
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
else if (marshalType.Type == "Array" || marshalType.Type == "Span" || marshalType.Type == "DataContainer" || marshalType.Type == "BytesContainer")
|
||||
else if (marshalType.IsArrayOrSpan)
|
||||
{
|
||||
string originalElementType = originalType.Substring(0, originalType.Length - 2);
|
||||
if (internalType)
|
||||
@@ -1748,17 +1949,17 @@ namespace Flax.Build.Bindings
|
||||
string originalElementTypeMarshaller = (!string.IsNullOrEmpty(apiType?.Namespace) ? $"{apiType?.Namespace}.Interop." : "") + $"{originalElementType}Marshaller";
|
||||
string originalElementTypeName = originalElementType.Substring(originalElementType.LastIndexOf('.') + 1); // Strip namespace
|
||||
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementTypeName}Internal";
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.ConvertArray((Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToSpan<{internalElementType}>(), {originalElementTypeMarshaller}.ToManaged) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(NativeInterop.ConvertArray(managed.{fieldInfo.Name}, {originalElementTypeMarshaller}.ToNative)), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<{internalElementType}> values = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<{internalElementType}>(); foreach (var value in values) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
toManagedContent.AppendLine($"{unmanagedField} != IntPtr.Zero ? NativeInterop.ConvertArray({unmanagedField}.AsSpan(), {originalElementTypeMarshaller}.ToManaged) : null;");
|
||||
toNativeContent.AppendLine($"{managedField}?.Length > 0 ? NativeInterop.ConvertArrayNative({managedField}.AsSpan(), {originalElementTypeMarshaller}.ToNative) : new NativeArray<{internalElementType}>();");
|
||||
freeContents.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ foreach (var value in {unmanagedField}.AsReadOnlySpan()) {{ {originalElementTypeMarshaller}.Free(value); }} {unmanagedField}.Free(); }}");
|
||||
freeContents2.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ foreach (var value in {unmanagedField}.AsReadOnlySpan()) {{ {originalElementTypeMarshaller}.NativeToManaged.Free(value); }} {unmanagedField}.Free(); }}");
|
||||
}
|
||||
else if (marshalType.GenericArgs[0].IsObjectRef)
|
||||
{
|
||||
// Array elements passed as GCHandles
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>(Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)) : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
toManagedContent.AppendLine($"{unmanagedField} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>({unmanagedField}) : null;");
|
||||
toNativeContent.AppendLine($"{managedField}?.Length > 0 ? NativeInterop.ManagedArrayToGCHandleWrappedArray(managed.{fieldInfo.Name}) : new NativeArray<IntPtr>();");
|
||||
freeContents.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ foreach (var ptr in {unmanagedField}.AsReadOnlySpan()) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} {unmanagedField}.Free(); }}");
|
||||
|
||||
// Permanent ScriptingObject handle is passed from native side, do not release it
|
||||
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); Span<IntPtr> ptrs = (Unsafe.As<ManagedArray>(handle.Target)).ToSpan<IntPtr>(); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(ptr).Free(); }} }} (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
@@ -1766,16 +1967,16 @@ namespace Flax.Build.Bindings
|
||||
else
|
||||
{
|
||||
// Blittable array elements
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? (Unsafe.As<ManagedArray>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target)).ToArray<{originalElementType}>() : null;");
|
||||
toNativeContent.AppendLine($"managed.{fieldInfo.Name}?.Length > 0 ? ManagedHandle.ToIntPtr(ManagedArray.WrapNewArray(managed.{fieldInfo.Name}), GCHandleType.Weak) : IntPtr.Zero;");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle handle = ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); (Unsafe.As<ManagedArray>(handle.Target)).Free(); handle.Free(); }}");
|
||||
toManagedContent.AppendLine($"{unmanagedField} != IntPtr.Zero ? {unmanagedField}.AsReadOnlySpan().ToArray() : null;");
|
||||
toNativeContent.AppendLine($"{managedField}?.Length > 0 ? new NativeArray<{originalElementType}>({managedField}.AsSpan()) : new NativeArray<{originalElementType}>();");
|
||||
freeContents.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ {unmanagedField}.Free(); }}");
|
||||
freeContents2.AppendLine($"if ({unmanagedField} != IntPtr.Zero) {{ {unmanagedField}.Free(); }}");
|
||||
}
|
||||
}
|
||||
else if (marshalType.Type == "Version")
|
||||
{
|
||||
toManagedContent.AppendLine($"unmanaged.{fieldInfo.Name} != IntPtr.Zero ? Unsafe.As<{originalType}>(ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Target) : null;");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}, GCHandleType.Weak);");
|
||||
toNativeContent.AppendLine($"ManagedHandle.ToIntPtr(managed.{fieldInfo.Name}/*, GCHandleType.Weak*/);");
|
||||
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ ManagedHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
|
||||
}
|
||||
@@ -1824,66 +2025,77 @@ namespace Flax.Build.Bindings
|
||||
toManagedContent.Append("return managed;");
|
||||
}
|
||||
|
||||
string hideInEditorAttribute = (buildData.Target != null & buildData.Target.IsEditor) ? "[HideInEditor]" : "";
|
||||
string marshalManagedType = structureInfo.Name;
|
||||
string marshalNativeType = $"{structureInfo.Name}Internal";
|
||||
contents.AppendLine(string.Join(Environment.NewLine + indent, (indent + $$"""
|
||||
/// <summary>
|
||||
/// Marshaller for type <see cref="{{structureInfo.Name}}"/>.
|
||||
/// Marshaller for type <see cref="{{marshalManagedType}}"/>.
|
||||
/// </summary>
|
||||
{{InsertHideInEditorSection()}}
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{structureInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
|
||||
{{hideInEditorAttribute}}
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementIn, typeof({{marshallerFullName}}.ManagedToNative))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementOut, typeof({{marshallerFullName}}.NativeToManaged))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerFullName}}.Bidirectional))]
|
||||
[CustomMarshaller(typeof({{marshalManagedType}}), MarshalMode.ElementRef, typeof({{marshallerFullName}}))]
|
||||
{{GenerateCSharpAccessLevel(structureInfo.Access)}}static unsafe class {{marshallerName}}
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
#pragma warning disable 618
|
||||
{{structContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
|
||||
{{InsertHideInEditorSection()}}
|
||||
{{hideInEditorAttribute}}
|
||||
public static class NativeToManaged
|
||||
{
|
||||
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{structureInfo.Name}}Internal unmanaged)
|
||||
public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{marshalNativeType}} unmanaged)
|
||||
{
|
||||
{{freeContents2.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
}
|
||||
{{InsertHideInEditorSection()}}
|
||||
{{hideInEditorAttribute}}
|
||||
public static class ManagedToNative
|
||||
{
|
||||
public static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{structureInfo.Name}}Internal unmanaged) => {{marshallerFullName}}.Free(unmanaged);
|
||||
public static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.ToManaged(unmanaged);
|
||||
public static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => {{marshallerFullName}}.ToNative(managed);
|
||||
public static void Free({{marshalNativeType}} unmanaged) => {{marshallerFullName}}.Free(unmanaged);
|
||||
}
|
||||
{{InsertHideInEditorSection()}}
|
||||
public struct Bidirectional
|
||||
{{hideInEditorAttribute}}
|
||||
public ref struct Bidirectional
|
||||
{
|
||||
{{structureInfo.Name}} managed;
|
||||
{{structureInfo.Name}}Internal unmanaged;
|
||||
public void FromManaged({{structureInfo.Name}} managed) => this.managed = managed;
|
||||
public {{structureInfo.Name}}Internal ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
|
||||
public void FromUnmanaged({{structureInfo.Name}}Internal unmanaged) => this.unmanaged = unmanaged;
|
||||
public {{structureInfo.Name}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
|
||||
public void Free() => NativeToManaged.Free(unmanaged);
|
||||
{{marshalManagedType}} managed;
|
||||
{{marshalNativeType}} unmanaged;
|
||||
public void FromManaged({{marshalManagedType}} managed) => this.managed = managed;
|
||||
public {{marshalNativeType}} ToUnmanaged() { unmanaged = {{marshallerFullName}}.ToNative(managed); return unmanaged; }
|
||||
public void FromUnmanaged({{marshalNativeType}} unmanaged)
|
||||
{
|
||||
if (!unmanaged.Equals(this.unmanaged))
|
||||
{{marshallerName}}.Free(this.unmanaged); // Release temporary handles before replacing them with permanent handles
|
||||
this.unmanaged = unmanaged;
|
||||
}
|
||||
public {{marshalManagedType}} ToManaged() { managed = {{marshallerFullName}}.ToManaged(unmanaged); return managed; }
|
||||
public void Free()
|
||||
{
|
||||
NativeToManaged.Free(unmanaged);
|
||||
}
|
||||
}
|
||||
internal static {{structureInfo.Name}} ConvertToManaged({{structureInfo.Name}}Internal unmanaged) => ToManaged(unmanaged);
|
||||
internal static {{structureInfo.Name}}Internal ConvertToUnmanaged({{structureInfo.Name}} managed) => ToNative(managed);
|
||||
internal static void Free({{structureInfo.Name}}Internal unmanaged)
|
||||
internal static {{marshalManagedType}} ConvertToManaged({{marshalNativeType}} unmanaged) => ToManaged(unmanaged);
|
||||
internal static {{marshalNativeType}} ConvertToUnmanaged({{marshalManagedType}} managed) => ToNative(managed);
|
||||
internal static void Free({{marshalNativeType}} unmanaged)
|
||||
{
|
||||
{{freeContents.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
|
||||
internal static {{structureInfo.Name}} ToManaged({{structureInfo.Name}}Internal unmanaged)
|
||||
internal static {{marshalManagedType}} ToManaged({{marshalNativeType}} unmanaged)
|
||||
{
|
||||
{{toManagedContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
internal static {{structureInfo.Name}}Internal ToNative({{structureInfo.Name}} managed)
|
||||
internal static {{marshalNativeType}} ToNative({{marshalManagedType}} managed)
|
||||
{
|
||||
{{toNativeContent.Replace("\n", Environment.NewLine + " ").ToString().TrimEnd()}}
|
||||
}
|
||||
@@ -1892,13 +2104,6 @@ namespace Flax.Build.Bindings
|
||||
}
|
||||
""").Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)));
|
||||
|
||||
string InsertHideInEditorSection()
|
||||
{
|
||||
return (buildData.Target != null & buildData.Target.IsEditor) ? $$"""
|
||||
[HideInEditor]
|
||||
""" : "";
|
||||
}
|
||||
|
||||
PutStringBuilder(toManagedContent);
|
||||
PutStringBuilder(toNativeContent);
|
||||
PutStringBuilder(freeContents);
|
||||
@@ -2231,7 +2436,7 @@ namespace Flax.Build.Bindings
|
||||
if (parameterInfo.IsOut)
|
||||
contents.Append("out ");
|
||||
else if (parameterInfo.IsRef)
|
||||
contents.Append("ref ");
|
||||
contents.Append("/*faffaf3*/ ref ");
|
||||
else if (parameterInfo.IsThis)
|
||||
contents.Append("this ");
|
||||
else if (parameterInfo.IsParams)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,30 +18,42 @@ namespace Flax.Build.Bindings
|
||||
public bool IsRef;
|
||||
public bool IsMoveRef;
|
||||
public bool IsPtr;
|
||||
|
||||
/// <summary>
|
||||
/// Is this a fixed-length type of array.
|
||||
/// </summary>
|
||||
public bool IsArray;
|
||||
|
||||
|
||||
public bool IsBitField;
|
||||
public int ArraySize;
|
||||
public int BitSize;
|
||||
public List<TypeInfo> GenericArgs;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this type is void.
|
||||
/// Is this a void type.
|
||||
/// </summary>
|
||||
public bool IsVoid => Type == "void" && !IsPtr;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this type is constant reference to a value.
|
||||
/// Is this a constant reference to a value.
|
||||
/// </summary>
|
||||
public bool IsConstRef => IsRef && IsConst;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this type is a reference to another object.
|
||||
/// Is this a reference to another FlaxEngine.Object.
|
||||
/// </summary>
|
||||
public bool IsObjectRef => (Type == "ScriptingObjectReference" ||
|
||||
Type == "AssetReference" ||
|
||||
Type == "WeakAssetReference" ||
|
||||
Type == "SoftAssetReference" ||
|
||||
Type == "SoftObjectReference") && GenericArgs != null;
|
||||
public bool IsObjectRef => Type is "ScriptingObjectReference"
|
||||
or "AssetReference"
|
||||
or "WeakAssetReference"
|
||||
or "SoftAssetReference"
|
||||
or "SoftObjectReference"
|
||||
&& GenericArgs != null;
|
||||
|
||||
/// <summary>
|
||||
/// Is this an inbuilt container type.
|
||||
/// </summary>
|
||||
public bool IsArrayOrSpan => Type is "Array" or "Span" or "DataContainer" or "BytesContainer";
|
||||
|
||||
public TypeInfo()
|
||||
{
|
||||
|
||||
@@ -487,6 +487,10 @@ namespace Flax.Build.Platforms
|
||||
}
|
||||
commonArgs.Add("/Zc:__cplusplus");
|
||||
|
||||
// Strict standards conformance mode
|
||||
commonArgs.Add("/permissive-");
|
||||
commonArgs.Add("/Zc:externC-"); // Required for WindowsMinimal.h
|
||||
|
||||
// Generate Intrinsic Functions
|
||||
if (compileEnvironment.IntrinsicFunctions)
|
||||
commonArgs.Add("/Oi");
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
var filePath = file.Replace('/', '\\'); // Normalize path
|
||||
var projectPath = Utilities.MakePathRelativeTo(filePath, projectDirectory);
|
||||
string linkPath = null;
|
||||
if (!filePath.StartsWith(rootPath))
|
||||
if (!filePath.StartsWith(rootPath)) // TODO: FIXME for FlaxEngine project in game solutions
|
||||
{
|
||||
// Create folder structure for project external files
|
||||
var sourceIndex = filePath.LastIndexOf(@"\Source\");
|
||||
|
||||
@@ -233,9 +233,17 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
files.AddRange(project.SourceFiles);
|
||||
if (project.SourceDirectories != null)
|
||||
{
|
||||
foreach (var folder in project.SourceDirectories)
|
||||
foreach (var sourceDirectory in project.SourceDirectories)
|
||||
{
|
||||
files.AddRange(Directory.GetFiles(folder, "*", SearchOption.AllDirectories));
|
||||
var subDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||
foreach (var directory in subDirectories)
|
||||
{
|
||||
// Skip common non-source directories
|
||||
if (directory == "obj" || directory == "Properties")
|
||||
continue;
|
||||
|
||||
files.AddRange(Directory.GetFiles(directory, "*", SearchOption.AllDirectories));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user