Merge remote-tracking branch 'origin/master' into visject_grid_snap

This commit is contained in:
Menotdan
2023-10-12 13:13:53 -04:00
66 changed files with 593 additions and 287 deletions

View File

@@ -36,7 +36,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
return; return;
var gizmos = gizmoOwner.Gizmos; var gizmos = gizmoOwner.Gizmos;
_gizmoMode = new ClothPaintingGizmoMode(); _gizmoMode = new ClothPaintingGizmoMode();
var projectCache = Editor.Instance.ProjectCache; var projectCache = Editor.Instance.ProjectCache;
if (projectCache.TryGetCustomData("ClothGizmoPaintValue", out var cachedPaintValue)) if (projectCache.TryGetCustomData("ClothGizmoPaintValue", out var cachedPaintValue))
_gizmoMode.PaintValue = JsonSerializer.Deserialize<float>(cachedPaintValue); _gizmoMode.PaintValue = JsonSerializer.Deserialize<float>(cachedPaintValue);
@@ -48,7 +48,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
_gizmoMode.BrushSize = JsonSerializer.Deserialize<float>(cachedBrushSize); _gizmoMode.BrushSize = JsonSerializer.Deserialize<float>(cachedBrushSize);
if (projectCache.TryGetCustomData("ClothGizmoBrushStrength", out var cachedBrushStrength)) if (projectCache.TryGetCustomData("ClothGizmoBrushStrength", out var cachedBrushStrength))
_gizmoMode.BrushStrength = JsonSerializer.Deserialize<float>(cachedBrushStrength); _gizmoMode.BrushStrength = JsonSerializer.Deserialize<float>(cachedBrushStrength);
gizmos.AddMode(_gizmoMode); gizmos.AddMode(_gizmoMode);
_prevMode = gizmos.ActiveMode; _prevMode = gizmos.ActiveMode;
gizmos.ActiveMode = _gizmoMode; gizmos.ActiveMode = _gizmoMode;

View File

@@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements
/// <summary> /// <summary>
/// [Deprecated on 26.05.2022, expires on 26.05.2024] /// [Deprecated on 26.05.2022, expires on 26.05.2024]
/// </summary> /// </summary>
[System.Obsolete("Deprecated in 1.4")] [System.Obsolete("Deprecated in 1.4, use ValueBox instead")]
public DoubleValueBox DoubleValue => ValueBox; public DoubleValueBox DoubleValue => ValueBox;
/// <summary> /// <summary>

View File

@@ -22,7 +22,7 @@ namespace FlaxEditor.CustomEditors.Elements
/// <summary> /// <summary>
/// [Deprecated on 26.05.2022, expires on 26.05.2024] /// [Deprecated on 26.05.2022, expires on 26.05.2024]
/// </summary> /// </summary>
[System.Obsolete("Deprecated in 1.4, ValueBox instead")] [System.Obsolete("Deprecated in 1.4, use ValueBox instead")]
public FloatValueBox FloatValue => ValueBox; public FloatValueBox FloatValue => ValueBox;
/// <summary> /// <summary>

View File

@@ -364,7 +364,7 @@ namespace FlaxEditor
{ {
foreach (var preview in activePreviews) foreach (var preview in activePreviews)
{ {
if (preview == loadingPreview || if (preview == loadingPreview ||
(preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control)))) (preview.Instance != null && (preview.Instance == control || preview.Instance.HasActorInHierarchy(control))))
{ {
// Link it to the prefab preview to see it in the editor // Link it to the prefab preview to see it in the editor

View File

@@ -482,8 +482,8 @@ namespace FlaxEditor.GUI
Focus(); Focus();
}); });
if (_selected != null) if (_selected != null)
{ {
var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path); var selectedAssetName = Path.GetFileNameWithoutExtension(_selected.Path);
popup.ScrollToAndHighlightItemByName(selectedAssetName); popup.ScrollToAndHighlightItemByName(selectedAssetName);
} }
} }

View File

@@ -72,7 +72,7 @@ namespace FlaxEditor.GUI.ContextMenu
// Hide parent CM popups and set itself as child // Hide parent CM popups and set itself as child
parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0))); parentContextMenu.ShowChild(ContextMenu, PointToParent(ParentContextMenu, new Float2(Width, 0)));
} }
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button) public override bool OnMouseUp(Float2 location, MouseButton button)
{ {

View File

@@ -293,7 +293,7 @@ namespace FlaxEditor.GUI.Dialogs
if (Root != null) if (Root != null)
{ {
bool shiftDown = Root.GetKey(KeyboardKeys.Shift); bool shiftDown = Root.GetKey(KeyboardKeys.Shift);
Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next); Root.Navigate(shiftDown ? NavDirection.Previous : NavDirection.Next);
} }
return true; return true;
} }

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.GUI.Input
: this(false, 0, 0) : this(false, 0, 0)
{ {
} }
/// <summary> /// <summary>
/// Init search box /// Init search box
/// </summary> /// </summary>
@@ -28,7 +28,7 @@ namespace FlaxEditor.GUI.Input
: base(isMultiline, x, y, width) : base(isMultiline, x, y, width)
{ {
WatermarkText = "Search..."; WatermarkText = "Search...";
ClearSearchButton = new Button ClearSearchButton = new Button
{ {
Parent = this, Parent = this,

View File

@@ -182,6 +182,7 @@ namespace FlaxEditor.GUI.Input
} }
SlidingEnd?.Invoke(); SlidingEnd?.Invoke();
Defocus(); Defocus();
Parent?.Focus();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -241,7 +241,7 @@ namespace FlaxEditor.GUI
{ {
DoubleClick?.Invoke(); DoubleClick?.Invoke();
RowDoubleClick?.Invoke(this); RowDoubleClick?.Invoke(this);
return base.OnMouseDoubleClick(location, button); return base.OnMouseDoubleClick(location, button);
} }

View File

@@ -104,7 +104,7 @@ namespace FlaxEditor.Modules
hint = "Too long name."; hint = "Too long name.";
return false; return false;
} }
if (item.IsFolder && shortName.EndsWith(".")) if (item.IsFolder && shortName.EndsWith("."))
{ {
hint = "Name cannot end with '.'"; hint = "Name cannot end with '.'";

View File

@@ -133,7 +133,7 @@ namespace FlaxEditor.Modules
return; return;
var actorsList = new List<Actor>(); var actorsList = new List<Actor>();
Utilities.Utils.GetActorsTree(actorsList, actor); Utilities.Utils.GetActorsTree(actorsList, actor);
var actions = new IUndoAction[actorsList.Count]; var actions = new IUndoAction[actorsList.Count];
for (int i = 0; i < actorsList.Count; i++) for (int i = 0; i < actorsList.Count; i++)
actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]); actions[i] = BreakPrefabLinkAction.Linked(actorsList[i]);

View File

@@ -453,7 +453,7 @@ namespace FlaxEditor.Modules
{ {
Editor.Windows.SceneWin.Focus(); Editor.Windows.SceneWin.Focus();
} }
// fix scene window layout // fix scene window layout
Editor.Windows.SceneWin.PerformLayout(); Editor.Windows.SceneWin.PerformLayout();
Editor.Windows.SceneWin.PerformLayout(); Editor.Windows.SceneWin.PerformLayout();
@@ -520,7 +520,7 @@ namespace FlaxEditor.Modules
Undo.AddAction(new MultiUndoAction(pasteAction, selectAction)); Undo.AddAction(new MultiUndoAction(pasteAction, selectAction));
OnSelectionChanged(); OnSelectionChanged();
} }
// Scroll to new selected node while pasting // Scroll to new selected node while pasting
Editor.Windows.SceneWin.ScrollToSelectedNode(); Editor.Windows.SceneWin.ScrollToSelectedNode();
} }
@@ -620,7 +620,7 @@ namespace FlaxEditor.Modules
Undo.AddAction(new MultiUndoAction(undoActions)); Undo.AddAction(new MultiUndoAction(undoActions));
OnSelectionChanged(); OnSelectionChanged();
} }
// Scroll to new selected node while duplicating // Scroll to new selected node while duplicating
Editor.Windows.SceneWin.ScrollToSelectedNode(); Editor.Windows.SceneWin.ScrollToSelectedNode();
} }

View File

@@ -332,7 +332,7 @@ namespace FlaxEditor.Modules
continue; continue;
scenes.Add(s); scenes.Add(s);
} }
// In play-mode Editor mocks the level streaming script // In play-mode Editor mocks the level streaming script
if (Editor.IsPlayMode) if (Editor.IsPlayMode)
{ {

View File

@@ -29,10 +29,10 @@ namespace FlaxEditor.Modules.SourceCodeEditing
private static bool CheckFunc(ScriptType scriptType) private static bool CheckFunc(ScriptType scriptType)
{ {
if (scriptType.IsStatic || if (scriptType.IsStatic ||
scriptType.IsGenericType || scriptType.IsGenericType ||
!scriptType.IsPublic || !scriptType.IsPublic ||
scriptType.HasAttribute(typeof(HideInEditorAttribute), true) || scriptType.HasAttribute(typeof(HideInEditorAttribute), true) ||
scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)) scriptType.HasAttribute(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false))
return false; return false;
var managedType = TypeUtils.GetType(scriptType); var managedType = TypeUtils.GetType(scriptType);

View File

@@ -299,7 +299,7 @@ namespace FlaxEditor.Modules
else else
text = "Ready"; text = "Ready";
if(ProgressVisible) if (ProgressVisible)
{ {
color = Style.Current.Statusbar.Loading; color = Style.Current.Statusbar.Loading;
} }
@@ -402,7 +402,7 @@ namespace FlaxEditor.Modules
{ {
UpdateStatusBar(); UpdateStatusBar();
} }
else if(ProgressVisible) else if (ProgressVisible)
{ {
UpdateStatusBar(); UpdateStatusBar();
} }
@@ -557,7 +557,7 @@ namespace FlaxEditor.Modules
cm.AddButton("Game Settings", () => cm.AddButton("Game Settings", () =>
{ {
var item = Editor.ContentDatabase.Find(GameSettings.GameSettingsAssetPath); var item = Editor.ContentDatabase.Find(GameSettings.GameSettingsAssetPath);
if(item != null) if (item != null)
Editor.ContentEditing.Open(item); Editor.ContentEditing.Open(item);
}); });

View File

@@ -16,7 +16,7 @@ namespace FlaxEditor.Progress
/// </summary> /// </summary>
/// <param name="handler">The calling handler.</param> /// <param name="handler">The calling handler.</param>
public delegate void ProgressDelegate(ProgressHandler handler); public delegate void ProgressDelegate(ProgressHandler handler);
/// <summary> /// <summary>
/// Progress failed handler event delegate /// Progress failed handler event delegate
/// </summary> /// </summary>
@@ -127,7 +127,7 @@ namespace FlaxEditor.Progress
{ {
if (!_isActive) if (!_isActive)
throw new InvalidOperationException("Already ended."); throw new InvalidOperationException("Already ended.");
_isActive = false; _isActive = false;
_progress = 0; _progress = 0;
_infoText = string.Empty; _infoText = string.Empty;

View File

@@ -34,6 +34,9 @@ namespace FlaxEditor.Surface.Archetypes
/// <seealso cref="FlaxEditor.Surface.SurfaceNode" /> /// <seealso cref="FlaxEditor.Surface.SurfaceNode" />
public class Sample : SurfaceNode public class Sample : SurfaceNode
{ {
private AssetSelect _assetSelect;
private Box _assetBox;
/// <inheritdoc /> /// <inheritdoc />
public Sample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) public Sample(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
: base(id, context, nodeArch, groupArch) : base(id, context, nodeArch, groupArch)
@@ -54,16 +57,42 @@ namespace FlaxEditor.Surface.Archetypes
base.OnSurfaceLoaded(action); base.OnSurfaceLoaded(action);
if (Surface != null) if (Surface != null)
{
_assetSelect = GetChild<AssetSelect>();
if (TryGetBox(8, out var box))
{
_assetBox = box;
_assetSelect.Visible = !_assetBox.HasAnyConnection;
}
UpdateTitle(); UpdateTitle();
}
} }
private void UpdateTitle() private void UpdateTitle()
{ {
var asset = Editor.Instance.ContentDatabase.Find((Guid)Values[0]); var asset = Editor.Instance.ContentDatabase.Find((Guid)Values[0]);
Title = asset?.ShortName ?? "Animation"; if (_assetBox != null)
Title = _assetBox.HasAnyConnection || asset == null ? "Animation" : asset.ShortName;
else
Title = asset?.ShortName ?? "Animation";
var style = Style.Current; var style = Style.Current;
Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160); Resize(Mathf.Max(230, style.FontLarge.MeasureText(Title).X + 30), 160);
} }
/// <inheritdoc />
public override void ConnectionTick(Box box)
{
base.ConnectionTick(box);
if (_assetBox == null)
return;
if (box.ID != _assetBox.ID)
return;
_assetSelect.Visible = !box.HasAnyConnection;
UpdateTitle();
}
} }
/// <summary> /// <summary>
@@ -305,7 +334,8 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(0, "Speed", true, typeof(float), 5, 1), NodeElementArchetype.Factory.Input(0, "Speed", true, typeof(float), 5, 1),
NodeElementArchetype.Factory.Input(1, "Loop", true, typeof(bool), 6, 2), NodeElementArchetype.Factory.Input(1, "Loop", true, typeof(bool), 6, 2),
NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 7, 3), NodeElementArchetype.Factory.Input(2, "Start Position", true, typeof(float), 7, 3),
NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 3, 0, typeof(FlaxEngine.Animation)), NodeElementArchetype.Factory.Input(3, "Animation Asset", true, typeof(FlaxEngine.Animation), 8),
NodeElementArchetype.Factory.Asset(0, Surface.Constants.LayoutOffsetY * 4, 0, typeof(FlaxEngine.Animation)),
} }
}, },
new NodeArchetype new NodeArchetype

View File

@@ -510,7 +510,7 @@ namespace FlaxEditor.Surface.Archetypes
for (var i = 0; i < elements.Length; i++) for (var i = 0; i < elements.Length; i++)
{ {
if(elements[i].Type != NodeElementType.Output) if (elements[i].Type != NodeElementType.Output)
continue; continue;
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint)) if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint))
@@ -533,7 +533,7 @@ namespace FlaxEditor.Surface.Archetypes
for (var i = 0; i < elements.Length; i++) for (var i = 0; i < elements.Length; i++)
{ {
if(elements[i].Type != NodeElementType.Input) if (elements[i].Type != NodeElementType.Input)
continue; continue;
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint)) if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint))
return true; return true;
@@ -725,7 +725,7 @@ namespace FlaxEditor.Surface.Archetypes
/// <inheritdoc /> /// <inheritdoc />
protected override bool UseNormalMaps => false; protected override bool UseNormalMaps => false;
internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
{ {
if (inputType == ScriptType.Object) if (inputType == ScriptType.Object)
@@ -743,7 +743,7 @@ namespace FlaxEditor.Surface.Archetypes
for (var i = 0; i < elements.Length; i++) for (var i = 0; i < elements.Length; i++)
{ {
if(elements[i].Type != NodeElementType.Output) if (elements[i].Type != NodeElementType.Output)
continue; continue;
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint)) if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, inputType, hint))
return true; return true;
@@ -765,7 +765,7 @@ namespace FlaxEditor.Surface.Archetypes
for (var i = 0; i < elements.Length; i++) for (var i = 0; i < elements.Length; i++)
{ {
if(elements[i].Type != NodeElementType.Input) if (elements[i].Type != NodeElementType.Input)
continue; continue;
if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint)) if (VisjectSurface.FullCastCheck(elements[i].ConnectionsType, outputType, hint))
return true; return true;
@@ -789,7 +789,7 @@ namespace FlaxEditor.Surface.Archetypes
/// <inheritdoc /> /// <inheritdoc />
protected override bool UseNormalMaps => false; protected override bool UseNormalMaps => false;
internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) internal new static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
{ {
if (inputType == ScriptType.Object) if (inputType == ScriptType.Object)
@@ -987,7 +987,7 @@ namespace FlaxEditor.Surface.Archetypes
_combobox.Width = Width - 50; _combobox.Width = Width - 50;
} }
} }
internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context) internal static bool IsOutputCompatible(NodeArchetype nodeArch, ScriptType inputType, ConnectionsHint hint, VisjectSurfaceContext context)
{ {
return inputType == ScriptType.Void; return inputType == ScriptType.Void;
@@ -997,7 +997,7 @@ namespace FlaxEditor.Surface.Archetypes
{ {
if (outputType == ScriptType.Void) if (outputType == ScriptType.Void)
return true; return true;
SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]); SurfaceParameter parameter = context.GetParameter((Guid)nodeArch.DefaultValues[0]);
ScriptType type = parameter?.Type ?? ScriptType.Null; ScriptType type = parameter?.Type ?? ScriptType.Null;

View File

@@ -141,7 +141,7 @@ namespace FlaxEditor.Surface
if (_connectionInstigator is Archetypes.Tools.RerouteNode) if (_connectionInstigator is Archetypes.Tools.RerouteNode)
{ {
if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true}) if (endPos.X < startPos.X && _lastInstigatorUnderMouse is null or Box { IsOutput: true })
{ {
actualStartPos = endPos; actualStartPos = endPos;
actualEndPos = startPos; actualEndPos = startPos;

View File

@@ -150,6 +150,12 @@ namespace FlaxEditor.Tools.Terrain
return; return;
} }
// Increase or decrease brush size with scroll
if (Input.GetKey(KeyboardKeys.Shift))
{
Mode.CurrentBrush.Size += dt * Mode.CurrentBrush.Size * Input.Mouse.ScrollDelta * 5f;
}
// Check if no terrain is selected // Check if no terrain is selected
var terrain = SelectedTerrain; var terrain = SelectedTerrain;
if (!terrain) if (!terrain)

View File

@@ -158,6 +158,12 @@ namespace FlaxEditor.Tools.Terrain
return; return;
} }
// Increase or decrease brush size with scroll
if (Input.GetKey(KeyboardKeys.Shift))
{
Mode.CurrentBrush.Size += dt * Mode.CurrentBrush.Size * Input.Mouse.ScrollDelta * 5f;
}
// Check if selected terrain was changed during painting // Check if selected terrain was changed during painting
if (terrain != _paintTerrain && IsPainting) if (terrain != _paintTerrain && IsPainting)
{ {

View File

@@ -49,7 +49,16 @@ namespace FlaxEditor.Actions
_scriptTypeName = script.TypeName; _scriptTypeName = script.TypeName;
_prefabId = script.PrefabID; _prefabId = script.PrefabID;
_prefabObjectId = script.PrefabObjectID; _prefabObjectId = script.PrefabObjectID;
_scriptData = FlaxEngine.Json.JsonSerializer.Serialize(script); try
{
_scriptData = FlaxEngine.Json.JsonSerializer.Serialize(script);
}
catch (Exception ex)
{
_scriptData = null;
Debug.LogError("Failed to serialize script data for Undo due to exception");
Debug.LogException(ex);
}
_parentId = script.Actor.ID; _parentId = script.Actor.ID;
_orderInParent = script.OrderInParent; _orderInParent = script.OrderInParent;
_enabled = script.Enabled; _enabled = script.Enabled;

View File

@@ -1119,7 +1119,12 @@ namespace FlaxEditor.Viewport
var win = (WindowRootControl)Root; var win = (WindowRootControl)Root;
// Get current mouse position in the view // Get current mouse position in the view
_viewMousePos = PointFromWindow(win.MousePosition); {
// When the window is not focused, the position in window does not return sane values
Float2 pos = PointFromWindow(win.MousePosition);
if (!float.IsInfinity(pos.LengthSquared))
_viewMousePos = pos;
}
// Update input // Update input
var window = win.Window; var window = win.Window;

View File

@@ -171,7 +171,7 @@ namespace FlaxEditor.Viewport.Previews
case DrawModes.Fill: case DrawModes.Fill:
clipsInView = 1.0f; clipsInView = 1.0f;
clipWidth = width; clipWidth = width;
samplesPerIndex = (uint)(samplesPerChannel / width); samplesPerIndex = (uint)(samplesPerChannel / width) * info.NumChannels;
break; break;
case DrawModes.Single: case DrawModes.Single:
clipsInView = Mathf.Min(clipsInView, 1.0f); clipsInView = Mathf.Min(clipsInView, 1.0f);

View File

@@ -97,7 +97,7 @@ namespace FlaxEditor
if (_highlightMaterial == null if (_highlightMaterial == null
|| (_highlights.Count == 0 && _highlightTriangles.Count == 0) || (_highlights.Count == 0 && _highlightTriangles.Count == 0)
|| renderContext.View.Pass == DrawPass.Depth || renderContext.View.Pass == DrawPass.Depth
) )
return; return;
Profiler.BeginEvent("ViewportDebugDrawData.OnDraw"); Profiler.BeginEvent("ViewportDebugDrawData.OnDraw");

View File

@@ -335,6 +335,22 @@ namespace FlaxEditor.Windows.Assets
} }
} }
/// <inheritdoc />
public override bool OnKeyDown(KeyboardKeys key)
{
if (base.OnKeyDown(key))
return true;
if (key == KeyboardKeys.Spacebar)
{
if (_previewSource?.State == AudioSource.States.Playing)
OnPause();
else
OnPlay();
}
return false;
}
/// <inheritdoc /> /// <inheritdoc />
public override bool UseLayoutData => true; public override bool UseLayoutData => true;

View File

@@ -153,7 +153,7 @@ namespace FlaxEditor.Windows.Assets
{ {
OnPasteAction(pasteAction); OnPasteAction(pasteAction);
} }
// Scroll to new selected node // Scroll to new selected node
ScrollToSelectedNode(); ScrollToSelectedNode();
} }
@@ -183,7 +183,7 @@ namespace FlaxEditor.Windows.Assets
{ {
OnPasteAction(pasteAction); OnPasteAction(pasteAction);
} }
// Scroll to new selected node // Scroll to new selected node
ScrollToSelectedNode(); ScrollToSelectedNode();
} }
@@ -334,7 +334,7 @@ namespace FlaxEditor.Windows.Assets
}, action2.ActionString); }, action2.ActionString);
action.Do(); action.Do();
Undo.AddAction(action); Undo.AddAction(action);
_treePanel.PerformLayout(); _treePanel.PerformLayout();
_treePanel.PerformLayout(); _treePanel.PerformLayout();
} }

View File

@@ -207,7 +207,7 @@ namespace FlaxEditor.Windows.Assets
InputActions.Add(options => options.Rename, Rename); InputActions.Add(options => options.Rename, Rename);
InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection); InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection);
} }
/// <summary> /// <summary>
/// Enables or disables vertical and horizontal scrolling on the tree panel. /// Enables or disables vertical and horizontal scrolling on the tree panel.
/// </summary> /// </summary>
@@ -257,7 +257,7 @@ namespace FlaxEditor.Windows.Assets
{ {
if (base.OnMouseUp(location, button)) if (base.OnMouseUp(location, button))
return true; return true;
if (button == MouseButton.Right && _treePanel.ContainsPoint(ref location)) if (button == MouseButton.Right && _treePanel.ContainsPoint(ref location))
{ {
_tree.Deselect(); _tree.Deselect();

View File

@@ -128,7 +128,7 @@ namespace FlaxEditor.Windows.Profiler
_tableRep.IsLayoutLocked = true; _tableRep.IsLayoutLocked = true;
RecycleTableRows(_tableRpc, _tableRowsCache); RecycleTableRows(_tableRpc, _tableRowsCache);
RecycleTableRows(_tableRep, _tableRowsCache); RecycleTableRows(_tableRep, _tableRowsCache);
var events = _events.Get(selectedFrame); var events = _events.Get(selectedFrame);
var rowCount = Int2.Zero; var rowCount = Int2.Zero;
if (events != null && events.Length != 0) if (events != null && events.Length != 0)
@@ -186,7 +186,7 @@ namespace FlaxEditor.Windows.Profiler
_tableRep.Visible = rowCount.Y != 0; _tableRep.Visible = rowCount.Y != 0;
_tableRpc.Children.Sort(SortRows); _tableRpc.Children.Sort(SortRows);
_tableRep.Children.Sort(SortRows); _tableRep.Children.Sort(SortRows);
_tableRpc.UnlockChildrenRecursive(); _tableRpc.UnlockChildrenRecursive();
_tableRpc.PerformLayout(); _tableRpc.PerformLayout();
_tableRep.UnlockChildrenRecursive(); _tableRep.UnlockChildrenRecursive();

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "AnimGraph.h" #include "AnimGraph.h"
#include "Engine/Core/Types/VariantValueCast.h"
#include "Engine/Content/Assets/Animation.h" #include "Engine/Content/Assets/Animation.h"
#include "Engine/Content/Assets/SkeletonMask.h" #include "Engine/Content/Assets/SkeletonMask.h"
#include "Engine/Content/Assets/AnimationGraphFunction.h" #include "Engine/Content/Assets/AnimationGraphFunction.h"
@@ -530,7 +531,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
transitionData.Position = 0; transitionData.Position = 0;
transitionData.Length = ZeroTolerance; transitionData.Length = ZeroTolerance;
} }
const bool useDefaultRule = EnumHasAnyFlags(transition.Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule); const bool useDefaultRule = EnumHasAnyFlags(transition.Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule);
if (transition.RuleGraph && !useDefaultRule) if (transition.RuleGraph && !useDefaultRule)
{ {
@@ -750,9 +751,16 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
// Animation // Animation
case 2: case 2:
{ {
const auto anim = node->Assets[0].As<Animation>(); auto anim = node->Assets[0].As<Animation>();
auto& bucket = context.Data->State[node->BucketIndex].Animation; auto& bucket = context.Data->State[node->BucketIndex].Animation;
// Override animation when animation reference box is connected
auto animationAssetBox = node->TryGetBox(8);
if (animationAssetBox && animationAssetBox->HasConnection())
{
anim = TVariantValueCast<Animation*>::Cast(tryGetValue(animationAssetBox, Value::Null));
}
switch (box->ID) switch (box->ID)
{ {
// Animation // Animation

View File

@@ -187,7 +187,6 @@ float AudioSource::GetTime() const
return 0.0f; return 0.0f;
float time = AudioBackend::Source::GetCurrentBufferTime(this); float time = AudioBackend::Source::GetCurrentBufferTime(this);
ASSERT(time >= 0.0f && time <= Clip->GetLength());
if (UseStreaming()) if (UseStreaming())
{ {

View File

@@ -27,7 +27,15 @@
#define MAX_INPUT_CHANNELS 2 #define MAX_INPUT_CHANNELS 2
#define MAX_OUTPUT_CHANNELS 8 #define MAX_OUTPUT_CHANNELS 8
#define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS) #define MAX_CHANNELS_MATRIX_SIZE (MAX_INPUT_CHANNELS*MAX_OUTPUT_CHANNELS)
#if ENABLE_ASSERTION
#define XAUDIO2_CHECK_ERROR(method) \
if (hr != 0) \
{ \
LOG(Error, "XAudio2 method {0} failed with error 0x{1:X} (at line {2})", TEXT(#method), (uint32)hr, __LINE__ - 1); \
}
#else
#define XAUDIO2_CHECK_ERROR(method)
#endif
#define FLAX_COORD_SCALE 0.01f // units are meters #define FLAX_COORD_SCALE 0.01f // units are meters
#define FLAX_DST_TO_XAUDIO(x) x * FLAX_COORD_SCALE #define FLAX_DST_TO_XAUDIO(x) x * FLAX_COORD_SCALE
#define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * FLAX_COORD_SCALE, vec.Y * FLAX_COORD_SCALE, vec.Z * FLAX_COORD_SCALE) #define FLAX_POS_TO_XAUDIO(vec) X3DAUDIO_VECTOR(vec.X * FLAX_COORD_SCALE, vec.Y * FLAX_COORD_SCALE, vec.Z * FLAX_COORD_SCALE)
@@ -104,7 +112,9 @@ namespace XAudio2
COM_DECLSPEC_NOTHROW void STDMETHODCALLTYPE OnVoiceError(THIS_ void* pBufferContext, HRESULT Error) override COM_DECLSPEC_NOTHROW void STDMETHODCALLTYPE OnVoiceError(THIS_ void* pBufferContext, HRESULT Error) override
{ {
#if ENABLE_ASSERTION
LOG(Warning, "IXAudio2VoiceCallback::OnVoiceError! Error: 0x{0:x}", Error); LOG(Warning, "IXAudio2VoiceCallback::OnVoiceError! Error: 0x{0:x}", Error);
#endif
} }
public: public:
@@ -121,7 +131,8 @@ namespace XAudio2
XAUDIO2_SEND_DESCRIPTOR Destination; XAUDIO2_SEND_DESCRIPTOR Destination;
float Pitch; float Pitch;
float Pan; float Pan;
float StartTime; float StartTimeForQueueBuffer;
float LastBufferStartTime;
float DopplerFactor; float DopplerFactor;
uint64 LastBufferStartSamplesPlayed; uint64 LastBufferStartSamplesPlayed;
int32 BuffersProcessed; int32 BuffersProcessed;
@@ -145,7 +156,8 @@ namespace XAudio2
Destination.pOutputVoice = nullptr; Destination.pOutputVoice = nullptr;
Pitch = 1.0f; Pitch = 1.0f;
Pan = 0.0f; Pan = 0.0f;
StartTime = 0.0f; StartTimeForQueueBuffer = 0.0f;
LastBufferStartTime = 0.0f;
IsDirty = false; IsDirty = false;
Is3D = false; Is3D = false;
IsPlaying = false; IsPlaying = false;
@@ -255,18 +267,18 @@ namespace XAudio2
buffer.pAudioData = aBuffer->Data.Get(); buffer.pAudioData = aBuffer->Data.Get();
buffer.AudioBytes = aBuffer->Data.Count(); buffer.AudioBytes = aBuffer->Data.Count();
if (aSource->StartTime > ZeroTolerance) if (aSource->StartTimeForQueueBuffer > ZeroTolerance)
{ {
buffer.PlayBegin = (UINT32)(aSource->StartTime * (aBuffer->Info.SampleRate * aBuffer->Info.NumChannels)); // Offset start position when playing buffer with a custom time offset
buffer.PlayLength = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels - buffer.PlayBegin; const uint32 bytesPerSample = aBuffer->Info.BitDepth / 8 * aBuffer->Info.NumChannels;
aSource->StartTime = 0; buffer.PlayBegin = (UINT32)(aSource->StartTimeForQueueBuffer * aBuffer->Info.SampleRate);
buffer.PlayLength = (buffer.AudioBytes / bytesPerSample) - buffer.PlayBegin;
aSource->LastBufferStartTime = aSource->StartTimeForQueueBuffer;
aSource->StartTimeForQueueBuffer = 0;
} }
const HRESULT hr = aSource->Voice->SubmitSourceBuffer(&buffer); const HRESULT hr = aSource->Voice->SubmitSourceBuffer(&buffer);
if (FAILED(hr)) XAUDIO2_CHECK_ERROR(SubmitSourceBuffer);
{
LOG(Warning, "XAudio2: Failed to submit source buffer (error: 0x{0:x})", hr);
}
} }
void VoiceCallback::OnBufferEnd(void* pBufferContext) void VoiceCallback::OnBufferEnd(void* pBufferContext)
@@ -375,7 +387,7 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
const auto& header = clip->AudioHeader; const auto& header = clip->AudioHeader;
auto& format = aSource->Format; auto& format = aSource->Format;
format.wFormatTag = WAVE_FORMAT_PCM; format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = source->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write) format.nChannels = clip->Is3D() ? 1 : header.Info.NumChannels; // 3d audio is always mono (AudioClip auto-converts before buffer write)
format.nSamplesPerSec = header.Info.SampleRate; format.nSamplesPerSec = header.Info.SampleRate;
format.wBitsPerSample = header.Info.BitDepth; format.wBitsPerSample = header.Info.BitDepth;
format.nBlockAlign = (WORD)(format.nChannels * (format.wBitsPerSample / 8)); format.nBlockAlign = (WORD)(format.nChannels * (format.wBitsPerSample / 8));
@@ -391,12 +403,10 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
1, 1,
&aSource->Destination &aSource->Destination
}; };
const HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList); HRESULT hr = XAudio2::Instance->CreateSourceVoice(&aSource->Voice, &aSource->Format, 0, 2.0f, &aSource->Callback, &sendList);
XAUDIO2_CHECK_ERROR(CreateSourceVoice);
if (FAILED(hr)) if (FAILED(hr))
{
LOG(Error, "Failed to create XAudio2 voice. Error: 0x{0:x}", hr);
return; return;
}
// Prepare source state // Prepare source state
aSource->Callback.Source = source; aSource->Callback.Source = source;
@@ -410,7 +420,8 @@ void AudioBackendXAudio2::Source_OnAdd(AudioSource* source)
aSource->DopplerFactor = source->GetDopplerFactor(); aSource->DopplerFactor = source->GetDopplerFactor();
aSource->UpdateTransform(source); aSource->UpdateTransform(source);
aSource->UpdateVelocity(source); aSource->UpdateVelocity(source);
aSource->Voice->SetVolume(source->GetVolume()); hr = aSource->Voice->SetVolume(source->GetVolume());
XAUDIO2_CHECK_ERROR(SetVolume);
// 0 is invalid ID so shift them // 0 is invalid ID so shift them
sourceID++; sourceID++;
@@ -451,7 +462,8 @@ void AudioBackendXAudio2::Source_VolumeChanged(AudioSource* source)
auto aSource = XAudio2::GetSource(source); auto aSource = XAudio2::GetSource(source);
if (aSource && aSource->Voice) if (aSource && aSource->Voice)
{ {
aSource->Voice->SetVolume(source->GetVolume()); const HRESULT hr = aSource->Voice->SetVolume(source->GetVolume());
XAUDIO2_CHECK_ERROR(SetVolume);
} }
} }
@@ -494,12 +506,18 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1]; XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Locker.Unlock(); XAudio2::Locker.Unlock();
HRESULT hr;
const bool isPlaying = source->IsActuallyPlayingSth(); const bool isPlaying = source->IsActuallyPlayingSth();
if (isPlaying) if (isPlaying)
aSource->Voice->Stop(); {
hr = aSource->Voice->Stop();
XAUDIO2_CHECK_ERROR(Stop);
}
aSource->Voice->FlushSourceBuffers(); hr = aSource->Voice->FlushSourceBuffers();
XAUDIO2_CHECK_ERROR(FlushSourceBuffers);
aSource->LastBufferStartSamplesPlayed = 0; aSource->LastBufferStartSamplesPlayed = 0;
aSource->LastBufferStartTime = 0;
aSource->BuffersProcessed = 0; aSource->BuffersProcessed = 0;
XAUDIO2_BUFFER buffer = { 0 }; XAUDIO2_BUFFER buffer = { 0 };
@@ -512,12 +530,15 @@ void AudioBackendXAudio2::Source_IsLoopingChanged(AudioSource* source)
const UINT32 totalSamples = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels; const UINT32 totalSamples = aBuffer->Info.NumSamples / aBuffer->Info.NumChannels;
buffer.PlayBegin = state.SamplesPlayed % totalSamples; buffer.PlayBegin = state.SamplesPlayed % totalSamples;
buffer.PlayLength = totalSamples - buffer.PlayBegin; buffer.PlayLength = totalSamples - buffer.PlayBegin;
aSource->StartTime = 0; aSource->StartTimeForQueueBuffer = 0;
XAudio2::QueueBuffer(aSource, source, bufferId, buffer); XAudio2::QueueBuffer(aSource, source, bufferId, buffer);
if (isPlaying) if (isPlaying)
aSource->Voice->Start(); {
hr = aSource->Voice->Start();
XAUDIO2_CHECK_ERROR(Start);
}
} }
void AudioBackendXAudio2::Source_SpatialSetupChanged(AudioSource* source) void AudioBackendXAudio2::Source_SpatialSetupChanged(AudioSource* source)
@@ -572,7 +593,8 @@ void AudioBackendXAudio2::Source_Play(AudioSource* source)
if (aSource && aSource->Voice && !aSource->IsPlaying) if (aSource && aSource->Voice && !aSource->IsPlaying)
{ {
// Play // Play
aSource->Voice->Start(); const HRESULT hr = aSource->Voice->Start();
XAUDIO2_CHECK_ERROR(Start);
aSource->IsPlaying = true; aSource->IsPlaying = true;
} }
} }
@@ -583,7 +605,8 @@ void AudioBackendXAudio2::Source_Pause(AudioSource* source)
if (aSource && aSource->Voice && aSource->IsPlaying) if (aSource && aSource->Voice && aSource->IsPlaying)
{ {
// Pause // Pause
aSource->Voice->Stop(); const HRESULT hr = aSource->Voice->Stop();
XAUDIO2_CHECK_ERROR(Stop);
aSource->IsPlaying = false; aSource->IsPlaying = false;
} }
} }
@@ -593,14 +616,18 @@ void AudioBackendXAudio2::Source_Stop(AudioSource* source)
auto aSource = XAudio2::GetSource(source); auto aSource = XAudio2::GetSource(source);
if (aSource && aSource->Voice) if (aSource && aSource->Voice)
{ {
aSource->StartTime = 0.0f; aSource->StartTimeForQueueBuffer = 0.0f;
aSource->LastBufferStartTime = 0.0f;
// Pause // Pause
aSource->Voice->Stop(); HRESULT hr = aSource->Voice->Stop();
XAUDIO2_CHECK_ERROR(Stop);
aSource->IsPlaying = false; aSource->IsPlaying = false;
// Unset streaming buffers to rewind // Unset streaming buffers to rewind
aSource->Voice->FlushSourceBuffers(); hr = aSource->Voice->FlushSourceBuffers();
XAUDIO2_CHECK_ERROR(FlushSourceBuffers);
Platform::Sleep(10); // TODO: find a better way to handle case when VoiceCallback::OnBufferEnd is called after source was stopped thus BuffersProcessed != 0, probably via buffers contexts ptrs
aSource->BuffersProcessed = 0; aSource->BuffersProcessed = 0;
aSource->Callback.PeekSamples(); aSource->Callback.PeekSamples();
} }
@@ -612,7 +639,7 @@ void AudioBackendXAudio2::Source_SetCurrentBufferTime(AudioSource* source, float
if (aSource) if (aSource)
{ {
// Store start time so next buffer submitted will start from here (assumes audio is stopped) // Store start time so next buffer submitted will start from here (assumes audio is stopped)
aSource->StartTime = value; aSource->StartTimeForQueueBuffer = value;
} }
} }
@@ -628,8 +655,9 @@ float AudioBackendXAudio2::Source_GetCurrentBufferTime(const AudioSource* source
aSource->Voice->GetState(&state); aSource->Voice->GetState(&state);
const uint32 numChannels = clipInfo.NumChannels; const uint32 numChannels = clipInfo.NumChannels;
const uint32 totalSamples = clipInfo.NumSamples / numChannels; const uint32 totalSamples = clipInfo.NumSamples / numChannels;
const uint32 sampleRate = clipInfo.SampleRate;// / clipInfo.NumChannels;
state.SamplesPlayed -= aSource->LastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin state.SamplesPlayed -= aSource->LastBufferStartSamplesPlayed % totalSamples; // Offset by the last buffer start to get time relative to its begin
time = aSource->StartTime + (state.SamplesPlayed % totalSamples) / static_cast<float>(Math::Max(1U, clipInfo.SampleRate)); time = aSource->LastBufferStartTime + (state.SamplesPlayed % totalSamples) / static_cast<float>(Math::Max(1U, sampleRate));
} }
return time; return time;
} }
@@ -697,10 +725,7 @@ void AudioBackendXAudio2::Source_DequeueProcessedBuffers(AudioSource* source)
if (aSource && aSource->Voice) if (aSource && aSource->Voice)
{ {
const HRESULT hr = aSource->Voice->FlushSourceBuffers(); const HRESULT hr = aSource->Voice->FlushSourceBuffers();
if (FAILED(hr)) XAUDIO2_CHECK_ERROR(FlushSourceBuffers);
{
LOG(Warning, "XAudio2: FlushSourceBuffers failed. Error: 0x{0:x}", hr);
}
aSource->BuffersProcessed = 0; aSource->BuffersProcessed = 0;
} }
} }
@@ -749,8 +774,7 @@ void AudioBackendXAudio2::Buffer_Write(uint32 bufferId, byte* samples, const Aud
XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1]; XAudio2::Buffer* aBuffer = XAudio2::Buffers[bufferId - 1];
XAudio2::Locker.Unlock(); XAudio2::Locker.Unlock();
const uint32 bytesPerSample = info.BitDepth / 8; const uint32 samplesLength = info.NumSamples * info.BitDepth / 8;
const int32 samplesLength = info.NumSamples * bytesPerSample;
aBuffer->Info = info; aBuffer->Info = info;
aBuffer->Data.Set(samples, samplesLength); aBuffer->Data.Set(samples, samplesLength);
@@ -779,7 +803,8 @@ void AudioBackendXAudio2::Base_SetVolume(float value)
{ {
if (XAudio2::MasteringVoice) if (XAudio2::MasteringVoice)
{ {
XAudio2::MasteringVoice->SetVolume(value); const HRESULT hr = XAudio2::MasteringVoice->SetVolume(value);
XAUDIO2_CHECK_ERROR(SetVolume);
} }
} }

View File

@@ -71,7 +71,7 @@ public:
/// <param name="g">The green channel value.</param> /// <param name="g">The green channel value.</param>
/// <param name="b">The blue channel value.</param> /// <param name="b">The blue channel value.</param>
/// <param name="a">The alpha channel value.</param> /// <param name="a">The alpha channel value.</param>
Color(float r, float g, float b, float a = 1) FORCE_INLINE Color(float r, float g, float b, float a = 1)
: R(r) : R(r)
, G(g) , G(g)
, B(b) , B(b)
@@ -203,7 +203,7 @@ public:
return Color(R - b.R, G - b.G, B - b.B, A - b.A); return Color(R - b.R, G - b.G, B - b.B, A - b.A);
} }
Color operator*(const Color& b) const FORCE_INLINE Color operator*(const Color& b) const
{ {
return Color(R * b.R, G * b.G, B * b.B, A * b.A); return Color(R * b.R, G * b.G, B * b.B, A * b.A);
} }

View File

@@ -1,10 +1,8 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "Half.h" #include "Half.h"
#include "Rectangle.h"
#include "Vector2.h"
#include "Vector3.h"
#include "Vector4.h" #include "Vector4.h"
#include "Rectangle.h"
#include "Color.h" #include "Color.h"
static_assert(sizeof(Half) == 2, "Invalid Half type size."); static_assert(sizeof(Half) == 2, "Invalid Half type size.");
@@ -16,12 +14,47 @@ Half2 Half2::Zero(0.0f, 0.0f);
Half3 Half3::Zero(0.0f, 0.0f, 0.0f); Half3 Half3::Zero(0.0f, 0.0f, 0.0f);
Half4 Half4::Zero(0.0f, 0.0f, 0.0f, 0.0f); Half4 Half4::Zero(0.0f, 0.0f, 0.0f, 0.0f);
Half2::Half2(const Float2& v) #if !USE_SSE_HALF_CONVERSION
Half Float16Compressor::Compress(float value)
{ {
X = Float16Compressor::Compress(v.X); Bits v, s;
Y = Float16Compressor::Compress(v.Y); v.f = value;
uint32 sign = v.si & signN;
v.si ^= sign;
sign >>= shiftSign; // logical shift
s.si = mulN;
s.si = static_cast<int32>(s.f * v.f); // correct subnormals
v.si ^= (s.si ^ v.si) & -(minN > v.si);
v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN));
v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN));
v.ui >>= shift; // logical shift
v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC);
v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC);
return v.ui | sign;
} }
float Float16Compressor::Decompress(Half value)
{
Bits v;
v.ui = value;
int32 sign = v.si & signC;
v.si ^= sign;
sign <<= shiftSign;
v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC);
v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC);
Bits s;
s.si = mulC;
s.f *= v.si;
const int32 mask = -(norC > v.si);
v.si <<= shift;
v.si ^= (s.si ^ v.si) & mask;
v.si |= sign;
return v.f;
}
#endif
Float2 Half2::ToFloat2() const Float2 Half2::ToFloat2() const
{ {
return Float2( return Float2(
@@ -30,13 +63,6 @@ Float2 Half2::ToFloat2() const
); );
} }
Half3::Half3(const Float3& v)
{
X = Float16Compressor::Compress(v.X);
Y = Float16Compressor::Compress(v.Y);
Z = Float16Compressor::Compress(v.Z);
}
Float3 Half3::ToFloat3() const Float3 Half3::ToFloat3() const
{ {
return Float3( return Float3(

View File

@@ -3,6 +3,8 @@
#pragma once #pragma once
#include "Math.h" #include "Math.h"
#include "Vector2.h"
#include "Vector3.h"
/// <summary> /// <summary>
/// Half-precision 16 bit floating point number consisting of a sign bit, a 5 bit biased exponent, and a 10 bit mantissa /// Half-precision 16 bit floating point number consisting of a sign bit, a 5 bit biased exponent, and a 10 bit mantissa
@@ -45,54 +47,23 @@ class FLAXENGINE_API Float16Compressor
static const int32 minD = minC - subC - 1; static const int32 minD = minC - subC - 1;
public: public:
static Half Compress(const float value)
{
#if USE_SSE_HALF_CONVERSION #if USE_SSE_HALF_CONVERSION
FORCE_INLINE static Half Compress(float value)
{
__m128 value1 = _mm_set_ss(value); __m128 value1 = _mm_set_ss(value);
__m128i value2 = _mm_cvtps_ph(value1, 0); __m128i value2 = _mm_cvtps_ph(value1, 0);
return static_cast<Half>(_mm_cvtsi128_si32(value2)); return static_cast<Half>(_mm_cvtsi128_si32(value2));
#else
Bits v, s;
v.f = value;
uint32 sign = v.si & signN;
v.si ^= sign;
sign >>= shiftSign; // logical shift
s.si = mulN;
s.si = static_cast<int32>(s.f * v.f); // correct subnormals
v.si ^= (s.si ^ v.si) & -(minN > v.si);
v.si ^= (infN ^ v.si) & -((infN > v.si) & (v.si > maxN));
v.si ^= (nanN ^ v.si) & -((nanN > v.si) & (v.si > infN));
v.ui >>= shift; // logical shift
v.si ^= ((v.si - maxD) ^ v.si) & -(v.si > maxC);
v.si ^= ((v.si - minD) ^ v.si) & -(v.si > subC);
return v.ui | sign;
#endif
} }
FORCE_INLINE static float Decompress(Half value)
static float Decompress(const Half value)
{ {
#if USE_SSE_HALF_CONVERSION
__m128i value1 = _mm_cvtsi32_si128(static_cast<int>(value)); __m128i value1 = _mm_cvtsi32_si128(static_cast<int>(value));
__m128 value2 = _mm_cvtph_ps(value1); __m128 value2 = _mm_cvtph_ps(value1);
return _mm_cvtss_f32(value2); return _mm_cvtss_f32(value2);
#else
Bits v;
v.ui = value;
int32 sign = v.si & signC;
v.si ^= sign;
sign <<= shiftSign;
v.si ^= ((v.si + minD) ^ v.si) & -(v.si > subC);
v.si ^= ((v.si + maxD) ^ v.si) & -(v.si > maxC);
Bits s;
s.si = mulC;
s.f *= v.si;
const int32 mask = -(norC > v.si);
v.si <<= shift;
v.si ^= (s.si ^ v.si) & mask;
v.si |= sign;
return v.f;
#endif
} }
#else
static Half Compress(float value);
static float Decompress(Half value);
#endif
}; };
/// <summary> /// <summary>
@@ -128,7 +99,7 @@ public:
/// </summary> /// </summary>
/// <param name="x">X component</param> /// <param name="x">X component</param>
/// <param name="y">Y component</param> /// <param name="y">Y component</param>
Half2(Half x, Half y) FORCE_INLINE Half2(Half x, Half y)
: X(x) : X(x)
, Y(y) , Y(y)
{ {
@@ -139,7 +110,7 @@ public:
/// </summary> /// </summary>
/// <param name="x">X component</param> /// <param name="x">X component</param>
/// <param name="y">Y component</param> /// <param name="y">Y component</param>
Half2(float x, float y) FORCE_INLINE Half2(float x, float y)
{ {
X = Float16Compressor::Compress(x); X = Float16Compressor::Compress(x);
Y = Float16Compressor::Compress(y); Y = Float16Compressor::Compress(y);
@@ -149,7 +120,11 @@ public:
/// Init /// Init
/// </summary> /// </summary>
/// <param name="v">X and Y components</param> /// <param name="v">X and Y components</param>
Half2(const Float2& v); FORCE_INLINE Half2(const Float2& v)
{
X = Float16Compressor::Compress(v.X);
Y = Float16Compressor::Compress(v.Y);
}
public: public:
Float2 ToFloat2() const; Float2 ToFloat2() const;
@@ -185,21 +160,26 @@ public:
public: public:
Half3() = default; Half3() = default;
Half3(Half x, Half y, Half z) FORCE_INLINE Half3(Half x, Half y, Half z)
: X(x) : X(x)
, Y(y) , Y(y)
, Z(z) , Z(z)
{ {
} }
Half3(const float x, const float y, const float z) FORCE_INLINE Half3(float x, float y, float z)
{ {
X = Float16Compressor::Compress(x); X = Float16Compressor::Compress(x);
Y = Float16Compressor::Compress(y); Y = Float16Compressor::Compress(y);
Z = Float16Compressor::Compress(z); Z = Float16Compressor::Compress(z);
} }
Half3(const Float3& v); FORCE_INLINE Half3(const Float3& v)
{
X = Float16Compressor::Compress(v.X);
Y = Float16Compressor::Compress(v.Y);
Z = Float16Compressor::Compress(v.Z);
}
public: public:
Float3 ToFloat3() const; Float3 ToFloat3() const;

View File

@@ -5,7 +5,6 @@
#include "Collections/Dictionary.h" #include "Collections/Dictionary.h"
#include "Engine/Engine/Time.h" #include "Engine/Engine/Time.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObject.h"
@@ -14,16 +13,15 @@ const Char* HertzSizesData[] = { TEXT("Hz"), TEXT("KHz"), TEXT("MHz"), TEXT("GHz
Span<const Char*> Utilities::Private::BytesSizes(BytesSizesData, ARRAY_COUNT(BytesSizesData)); Span<const Char*> Utilities::Private::BytesSizes(BytesSizesData, ARRAY_COUNT(BytesSizesData));
Span<const Char*> Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(HertzSizesData)); Span<const Char*> Utilities::Private::HertzSizes(HertzSizesData, ARRAY_COUNT(HertzSizesData));
namespace ObjectsRemovalServiceImpl namespace
{ {
CriticalSection PoolLocker; CriticalSection PoolLocker;
DateTime LastUpdate; DateTime LastUpdate;
float LastUpdateGameTime; float LastUpdateGameTime;
Dictionary<Object*, float> Pool(8192); Dictionary<Object*, float> Pool(8192);
uint64 PoolCounter = 0;
} }
using namespace ObjectsRemovalServiceImpl;
class ObjectsRemoval : public EngineService class ObjectsRemoval : public EngineService
{ {
public: public:
@@ -64,6 +62,7 @@ void ObjectsRemovalService::Add(Object* obj, float timeToLive, bool useGameTime)
PoolLocker.Lock(); PoolLocker.Lock();
Pool[obj] = timeToLive; Pool[obj] = timeToLive;
PoolCounter++;
PoolLocker.Unlock(); PoolLocker.Unlock();
} }
@@ -72,6 +71,7 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta)
PROFILE_CPU(); PROFILE_CPU();
PoolLocker.Lock(); PoolLocker.Lock();
PoolCounter = 0;
// Update timeouts and delete objects that timed out // Update timeouts and delete objects that timed out
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
@@ -90,6 +90,24 @@ void ObjectsRemovalService::Flush(float dt, float gameDelta)
} }
} }
// If any object was added to the pool while removing objects (by this thread) then retry removing any nested objects (but without delta time)
if (PoolCounter != 0)
{
RETRY:
PoolCounter = 0;
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value <= 0.0f)
{
Object* obj = i->Key;
Pool.Remove(i);
obj->OnDeleteObject();
}
}
if (PoolCounter != 0)
goto RETRY;
}
PoolLocker.Unlock(); PoolLocker.Unlock();
} }
@@ -121,7 +139,7 @@ void ObjectsRemoval::Dispose()
// Delete all remaining objects // Delete all remaining objects
{ {
ScopeLock lock(PoolLocker); PoolLocker.Lock();
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
{ {
Object* obj = i->Key; Object* obj = i->Key;
@@ -129,6 +147,7 @@ void ObjectsRemoval::Dispose()
obj->OnDeleteObject(); obj->OnDeleteObject();
} }
Pool.Clear(); Pool.Clear();
PoolLocker.Unlock();
} }
} }

View File

@@ -857,36 +857,25 @@ namespace FlaxEngine.Interop
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, IntPtr valuePtr) internal static void FieldGetValueReference(ManagedHandle fieldOwnerHandle, ManagedHandle fieldHandle, int fieldOffset, IntPtr valuePtr)
{ {
object fieldOwner = fieldOwnerHandle.Target; object fieldOwner = fieldOwnerHandle.Target;
IntPtr fieldRef;
#if USE_AOT
FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target); FieldHolder field = Unsafe.As<FieldHolder>(fieldHandle.Target);
fieldRef = IntPtr.Zero;
Debug.LogError("Not supported FieldGetValueReference");
#else
if (fieldOwner.GetType().IsValueType) if (fieldOwner.GetType().IsValueType)
{ {
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner); fieldRef = FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
} }
else else
{ {
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(field.fieldOffset, ref fieldOwner); fieldRef = FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
}
}
[UnmanagedCallersOnly]
internal static void FieldGetValueReferenceWithOffset(ManagedHandle fieldOwnerHandle, int fieldOffset, IntPtr valuePtr)
{
object fieldOwner = fieldOwnerHandle.Target;
if (fieldOwner.GetType().IsValueType)
{
ref IntPtr fieldRef = ref FieldHelper.GetValueTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
}
else
{
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<object, IntPtr>(fieldOffset, ref fieldOwner);
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
} }
#endif
Unsafe.Write<IntPtr>(valuePtr.ToPointer(), fieldRef);
} }
[UnmanagedCallersOnly] [UnmanagedCallersOnly]

View File

@@ -119,6 +119,7 @@ namespace FlaxEngine.Interop
{ {
} }
#if !USE_AOT
// Cache offsets to frequently accessed fields of FlaxEngine.Object // Cache offsets to frequently accessed fields of FlaxEngine.Object
private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); private static int unmanagedPtrFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__unmanagedPtr", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF); private static int internalIdFieldOffset = IntPtr.Size + (Unsafe.Read<int>((typeof(FlaxEngine.Object).GetField("__internalId", BindingFlags.Instance | BindingFlags.NonPublic).FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF);
@@ -150,6 +151,7 @@ namespace FlaxEngine.Interop
object obj = typeHolder.CreateScriptingObject(unmanagedPtr, idPtr); object obj = typeHolder.CreateScriptingObject(unmanagedPtr, idPtr);
return ManagedHandle.Alloc(obj); return ManagedHandle.Alloc(obj);
} }
#endif
internal static void* NativeAlloc(int byteCount) internal static void* NativeAlloc(int byteCount)
{ {
@@ -429,6 +431,9 @@ namespace FlaxEngine.Interop
/// </summary> /// </summary>
internal static int GetFieldOffset(FieldInfo field, Type type) internal static int GetFieldOffset(FieldInfo field, Type type)
{ {
if (field.IsLiteral)
return 0;
// Get the address of the field, source: https://stackoverflow.com/a/56512720 // Get the address of the field, source: https://stackoverflow.com/a/56512720
int fieldOffset = Unsafe.Read<int>((field.FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF; int fieldOffset = Unsafe.Read<int>((field.FieldHandle.Value + 4 + IntPtr.Size).ToPointer()) & 0xFFFFFF;
if (!type.IsValueType) if (!type.IsValueType)
@@ -436,6 +441,23 @@ namespace FlaxEngine.Interop
return fieldOffset; return fieldOffset;
} }
#if USE_AOT
/// <summary>
/// Helper utility to set field of the referenced value via reflection.
/// </summary>
internal static void SetReferenceTypeField<T>(FieldInfo field, ref T fieldOwner, object fieldValue)
{
if (typeof(T).IsValueType)
{
// Value types need setting via boxed object to properly propagate value
object fieldOwnerBoxed = fieldOwner;
field.SetValue(fieldOwnerBoxed, fieldValue);
fieldOwner = (T)fieldOwnerBoxed;
}
else
field.SetValue(fieldOwner, fieldValue);
}
#else
/// <summary> /// <summary>
/// Returns a reference to the value of the field. /// Returns a reference to the value of the field.
/// </summary> /// </summary>
@@ -462,6 +484,7 @@ namespace FlaxEngine.Interop
byte* fieldPtr = (byte*)Unsafe.As<T, IntPtr>(ref fieldOwner) + fieldOffset; byte* fieldPtr = (byte*)Unsafe.As<T, IntPtr>(ref fieldOwner) + fieldOffset;
return ref Unsafe.AsRef<TField>(fieldPtr); return ref Unsafe.AsRef<TField>(fieldPtr);
} }
#endif
} }
/// <summary> /// <summary>
@@ -735,29 +758,49 @@ namespace FlaxEngine.Interop
private static void ToManagedFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct private static void ToManagedFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
{ {
#if USE_AOT
IntPtr fieldValue = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#else
ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner); ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()); fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
#endif
fieldSize = IntPtr.Size; fieldSize = IntPtr.Size;
} }
private static void ToManagedFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class private static void ToManagedFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
{ {
#if USE_AOT
IntPtr fieldValue = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#else
ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner); ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()); fieldValueRef = Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer());
#endif
fieldSize = IntPtr.Size; fieldSize = IntPtr.Size;
} }
private static void ToNativeFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct private static void ToNativeFieldPointerValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
{ {
ref IntPtr fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner); #if USE_AOT
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValueRef); object boxed = field.GetValue(fieldOwner);
IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed));
#else
IntPtr fieldValue = FieldHelper.GetValueTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
#endif
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValue);
fieldSize = IntPtr.Size; fieldSize = IntPtr.Size;
} }
private static void ToNativeFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class private static void ToNativeFieldPointerReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
{ {
ref IntPtr fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner); #if USE_AOT
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValueRef); object boxed = field.GetValue(fieldOwner);
IntPtr fieldValue = new IntPtr(Pointer.Unbox(boxed));
#else
IntPtr fieldValue = FieldHelper.GetReferenceTypeFieldReference<T, IntPtr>(fieldOffset, ref fieldOwner);
#endif
Unsafe.Write<IntPtr>(nativeFieldPtr.ToPointer(), fieldValue);
fieldSize = IntPtr.Size; fieldSize = IntPtr.Size;
} }
@@ -799,8 +842,15 @@ namespace FlaxEngine.Interop
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
} }
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField>.ToManaged(ref fieldValueRef, nativeFieldPtr, false); TField fieldValue = default;
#else
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToManaged(ref fieldValue, nativeFieldPtr, false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -813,8 +863,15 @@ namespace FlaxEngine.Interop
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
} }
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField>.ToManaged(ref fieldValueRef, nativeFieldPtr, false); TField fieldValue = default;
#else
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToManaged(ref fieldValue, nativeFieldPtr, false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -825,8 +882,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false); TField[] fieldValue = (TField[])field.GetValue(fieldOwner);
#else
ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -837,8 +901,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false); TField[] fieldValue = null;
#else
ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -852,11 +923,11 @@ namespace FlaxEngine.Interop
} }
#if USE_AOT #if USE_AOT
TField fieldValueRef = (TField)field.GetValue(fieldOwner); TField fieldValue = (TField)field.GetValue(fieldOwner);
#else #else
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif #endif
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr); MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
} }
internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -870,11 +941,11 @@ namespace FlaxEngine.Interop
} }
#if USE_AOT #if USE_AOT
TField fieldValueRef = (TField)field.GetValue(fieldOwner); TField fieldValue = (TField)field.GetValue(fieldOwner);
#else #else
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif #endif
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr); MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
} }
} }
@@ -904,8 +975,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false); TField fieldValue = null;
#else
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class internal static void ToManagedFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -915,8 +993,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false); TField fieldValue = default;
#else
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct internal static void ToManagedFieldArrayValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -926,8 +1011,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false); TField[] fieldValue = null;
#else
ref TField[] fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class internal static void ToManagedFieldArrayReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -937,8 +1029,15 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField[] fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField[]>.ToManaged(ref fieldValueRef, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false); TField[] fieldValue = null;
#else
ref TField[] fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField[]>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField[]>.ToManaged(ref fieldValue, Unsafe.Read<IntPtr>(nativeFieldPtr.ToPointer()), false);
#if USE_AOT
FieldHelper.SetReferenceTypeField(field, ref fieldOwner, fieldValue);
#endif
} }
internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct internal static void ToNativeFieldValueType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : struct
@@ -948,8 +1047,12 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr); TField fieldValue = (TField)field.GetValue(fieldOwner);
#else
ref TField fieldValue = ref FieldHelper.GetValueTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
} }
internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class internal static void ToNativeFieldReferenceType(FieldInfo field, int fieldOffset, ref T fieldOwner, IntPtr nativeFieldPtr, out int fieldSize) // where T : class
@@ -959,8 +1062,12 @@ namespace FlaxEngine.Interop
nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size); nativeFieldPtr = EnsureAlignment(nativeFieldPtr, IntPtr.Size);
fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32(); fieldSize += (nativeFieldPtr - fieldStartPtr).ToInt32();
ref TField fieldValueRef = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner); #if USE_AOT
MarshalHelper<TField>.ToNative(ref fieldValueRef, nativeFieldPtr); TField fieldValue = (TField)field.GetValue(fieldOwner);
#else
ref TField fieldValue = ref FieldHelper.GetReferenceTypeFieldReference<T, TField>(fieldOffset, ref fieldOwner);
#endif
MarshalHelper<TField>.ToNative(ref fieldValue, nativeFieldPtr);
} }
} }
} }
@@ -1044,7 +1151,7 @@ namespace FlaxEngine.Interop
var fields = MarshalHelper<T>.marshallableFields; var fields = MarshalHelper<T>.marshallableFields;
var offsets = MarshalHelper<T>.marshallableFieldOffsets; var offsets = MarshalHelper<T>.marshallableFieldOffsets;
var marshallers = MarshalHelper<T>.toNativeFieldMarshallers; var marshallers = MarshalHelper<T>.toNativeFieldMarshallers;
for (int i = 0; i < MarshalHelper<T>.marshallableFields.Length; i++) for (int i = 0; i < fields.Length; i++)
{ {
marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize); marshallers[i](fields[i], offsets[i], ref managedValue, nativePtr, out int fieldSize);
nativePtr += fieldSize; nativePtr += fieldSize;
@@ -1306,11 +1413,13 @@ namespace FlaxEngine.Interop
return RuntimeHelpers.GetUninitializedObject(wrappedType); return RuntimeHelpers.GetUninitializedObject(wrappedType);
} }
#if !USE_AOT
internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr) internal object CreateScriptingObject(IntPtr unmanagedPtr, IntPtr idPtr)
{ {
object obj = CreateObject(); object obj = RuntimeHelpers.GetUninitializedObject(wrappedType);
if (obj is Object) if (obj is Object)
{ {
// TODO: use UnsafeAccessorAttribute on .NET 8 and use this path on all platforms (including non-Desktop, see MCore::ScriptingObject::CreateScriptingObject)
{ {
ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<IntPtr>(unmanagedPtrFieldOffset, ref obj); ref IntPtr fieldRef = ref FieldHelper.GetReferenceTypeFieldReference<IntPtr>(unmanagedPtrFieldOffset, ref obj);
fieldRef = unmanagedPtr; fieldRef = unmanagedPtr;
@@ -1331,8 +1440,9 @@ namespace FlaxEngine.Interop
return obj; return obj;
} }
#endif
public static implicit operator Type(TypeHolder holder) => holder?.type ?? null; public static implicit operator Type(TypeHolder holder) => holder?.type;
public bool Equals(TypeHolder other) => type == other.type; public bool Equals(TypeHolder other) => type == other.type;
public bool Equals(Type other) => type == other; public bool Equals(Type other) => type == other;
public override int GetHashCode() => type.GetHashCode(); public override int GetHashCode() => type.GetHashCode();

View File

@@ -259,6 +259,11 @@ API_STRUCT() struct GPULimits
/// </summary> /// </summary>
API_FIELD() bool HasDepthAsSRV; API_FIELD() bool HasDepthAsSRV;
/// <summary>
/// True if device supports depth buffer clipping (see GPUPipelineState::Description::DepthClipEnable).
/// </summary>
API_FIELD() bool HasDepthClip;
/// <summary> /// <summary>
/// True if device supports depth buffer texture as a readonly depth buffer (can be sampled in the shader while performing depth-test). /// True if device supports depth buffer texture as a readonly depth buffer (can be sampled in the shader while performing depth-test).
/// </summary> /// </summary>

View File

@@ -339,7 +339,7 @@ namespace FlaxEngine
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param> /// <param name="colors">The vertex colors (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(Vector3[] vertices, int[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null) public void UpdateMesh(Vector3[] vertices, int[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
@@ -357,7 +357,7 @@ namespace FlaxEngine
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param> /// <param name="colors">The vertex colors (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(List<Vector3> vertices, List<int> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null) public void UpdateMesh(List<Vector3> vertices, List<int> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
@@ -375,7 +375,7 @@ namespace FlaxEngine
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param> /// <param name="colors">The vertex colors (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(Vector3[] vertices, uint[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null) public void UpdateMesh(Vector3[] vertices, uint[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
@@ -393,7 +393,7 @@ namespace FlaxEngine
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param> /// <param name="colors">The vertex colors (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(List<Vector3> vertices, List<uint> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null) public void UpdateMesh(List<Vector3> vertices, List<uint> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
@@ -411,7 +411,7 @@ namespace FlaxEngine
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param> /// <param name="colors">The vertex colors (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null) public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null, Color32[] colors = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);
@@ -429,7 +429,7 @@ namespace FlaxEngine
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
/// <param name="colors">The vertex colors (per vertex).</param> /// <param name="colors">The vertex colors (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(List<Vector3> vertices, List<ushort> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null) public void UpdateMesh(List<Vector3> vertices, List<ushort> triangles, List<Vector3> normals = null, List<Vector3> tangents = null, List<Vector2> uv = null, List<Color32> colors = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors); UpdateMesh(Utils.ConvertCollection(vertices), triangles, Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv), colors);

View File

@@ -216,7 +216,7 @@ namespace FlaxEngine
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(Vector3[] vertices, int[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null) public void UpdateMesh(Vector3[] vertices, int[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv)); UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv));
@@ -235,7 +235,7 @@ namespace FlaxEngine
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The normal vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(Vector3[] vertices, uint[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null) public void UpdateMesh(Vector3[] vertices, uint[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv)); UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv));
@@ -254,7 +254,7 @@ namespace FlaxEngine
/// <param name="normals">The normal vectors (per vertex).</param> /// <param name="normals">The normal vectors (per vertex).</param>
/// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param> /// <param name="tangents">The tangent vectors (per vertex). Use null to compute them from normal vectors.</param>
/// <param name="uv">The texture coordinates (per vertex).</param> /// <param name="uv">The texture coordinates (per vertex).</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null) public void UpdateMesh(Vector3[] vertices, ushort[] triangles, Int4[] blendIndices, Vector4[] blendWeights, Vector3[] normals = null, Vector3[] tangents = null, Vector2[] uv = null)
{ {
UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv)); UpdateMesh(Utils.ConvertCollection(vertices), triangles, blendIndices, Utils.ConvertCollection(blendWeights), Utils.ConvertCollection(normals), Utils.ConvertCollection(tangents), Utils.ConvertCollection(uv));

View File

@@ -359,6 +359,7 @@ bool GPUDeviceDX11::Init()
limits.HasAppendConsumeBuffers = true; limits.HasAppendConsumeBuffers = true;
limits.HasSeparateRenderTargetBlendState = true; limits.HasSeparateRenderTargetBlendState = true;
limits.HasDepthAsSRV = true; limits.HasDepthAsSRV = true;
limits.HasDepthClip = true;
limits.HasReadOnlyDepth = true; limits.HasReadOnlyDepth = true;
limits.HasMultisampleDepthAsSRV = true; limits.HasMultisampleDepthAsSRV = true;
limits.HasTypedUAVLoad = featureDataD3D11Options2.TypedUAVLoadAdditionalFormats != 0; limits.HasTypedUAVLoad = featureDataD3D11Options2.TypedUAVLoadAdditionalFormats != 0;
@@ -382,6 +383,7 @@ bool GPUDeviceDX11::Init()
limits.HasAppendConsumeBuffers = false; limits.HasAppendConsumeBuffers = false;
limits.HasSeparateRenderTargetBlendState = false; limits.HasSeparateRenderTargetBlendState = false;
limits.HasDepthAsSRV = false; limits.HasDepthAsSRV = false;
limits.HasDepthClip = true;
limits.HasReadOnlyDepth = createdFeatureLevel == D3D_FEATURE_LEVEL_10_1; limits.HasReadOnlyDepth = createdFeatureLevel == D3D_FEATURE_LEVEL_10_1;
limits.HasMultisampleDepthAsSRV = false; limits.HasMultisampleDepthAsSRV = false;
limits.HasTypedUAVLoad = false; limits.HasTypedUAVLoad = false;

View File

@@ -381,6 +381,7 @@ bool GPUDeviceDX12::Init()
limits.HasAppendConsumeBuffers = true; limits.HasAppendConsumeBuffers = true;
limits.HasSeparateRenderTargetBlendState = true; limits.HasSeparateRenderTargetBlendState = true;
limits.HasDepthAsSRV = true; limits.HasDepthAsSRV = true;
limits.HasDepthClip = true;
limits.HasReadOnlyDepth = true; limits.HasReadOnlyDepth = true;
limits.HasMultisampleDepthAsSRV = true; limits.HasMultisampleDepthAsSRV = true;
limits.HasTypedUAVLoad = options.TypedUAVLoadAdditionalFormats != 0; limits.HasTypedUAVLoad = options.TypedUAVLoadAdditionalFormats != 0;

View File

@@ -50,18 +50,7 @@ bool GPUDeviceNull::Init()
// Init device limits // Init device limits
{ {
auto& limits = Limits; auto& limits = Limits;
limits.HasCompute = false; Platform::MemoryClear(&limits, sizeof(limits));
limits.HasTessellation = false;
limits.HasGeometryShaders = false;
limits.HasInstancing = false;
limits.HasVolumeTextureRendering = false;
limits.HasDrawIndirect = false;
limits.HasAppendConsumeBuffers = false;
limits.HasSeparateRenderTargetBlendState = false;
limits.HasDepthAsSRV = false;
limits.HasReadOnlyDepth = false;
limits.HasMultisampleDepthAsSRV = false;
limits.HasTypedUAVLoad = false;
limits.MaximumMipLevelsCount = 14; limits.MaximumMipLevelsCount = 14;
limits.MaximumTexture1DSize = 8192; limits.MaximumTexture1DSize = 8192;
limits.MaximumTexture1DArraySize = 512; limits.MaximumTexture1DArraySize = 512;
@@ -70,11 +59,8 @@ bool GPUDeviceNull::Init()
limits.MaximumTexture3DSize = 2048; limits.MaximumTexture3DSize = 2048;
limits.MaximumTextureCubeSize = 16384; limits.MaximumTextureCubeSize = 16384;
limits.MaximumSamplerAnisotropy = 1; limits.MaximumSamplerAnisotropy = 1;
for (int32 i = 0; i < static_cast<int32>(PixelFormat::MAX); i++) for (int32 i = 0; i < static_cast<int32>(PixelFormat::MAX); i++)
{
FeaturesPerFormat[i] = FormatFeatures(static_cast<PixelFormat>(i), MSAALevel::None, FormatSupport::None); FeaturesPerFormat[i] = FormatFeatures(static_cast<PixelFormat>(i), MSAALevel::None, FormatSupport::None);
}
} }
// Create main context // Create main context

View File

@@ -1210,16 +1210,16 @@ void GPUContextVulkan::ResolveMultisample(GPUTexture* sourceMultisampleTexture,
void GPUContextVulkan::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex) void GPUContextVulkan::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex)
{ {
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
OnDrawCall(); OnDrawCall();
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
vkCmdDraw(cmdBuffer->GetHandle(), verticesCount, instanceCount, startVertex, startInstance); vkCmdDraw(cmdBuffer->GetHandle(), verticesCount, instanceCount, startVertex, startInstance);
RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3); RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3);
} }
void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex) void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex)
{ {
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
OnDrawCall(); OnDrawCall();
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
vkCmdDrawIndexed(cmdBuffer->GetHandle(), indicesCount, instanceCount, startIndex, startVertex, startInstance); vkCmdDrawIndexed(cmdBuffer->GetHandle(), indicesCount, instanceCount, startIndex, startVertex, startInstance);
RENDER_STAT_DRAW_CALL(0, indicesCount / 3 * instanceCount); RENDER_STAT_DRAW_CALL(0, indicesCount / 3 * instanceCount);
} }
@@ -1227,10 +1227,9 @@ void GPUContextVulkan::DrawIndexedInstanced(uint32 indicesCount, uint32 instance
void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs)
{ {
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
OnDrawCall();
auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs; auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs;
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
OnDrawCall();
vkCmdDrawIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndirectCommand)); vkCmdDrawIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndirectCommand));
RENDER_STAT_DRAW_CALL(0, 0); RENDER_STAT_DRAW_CALL(0, 0);
} }
@@ -1238,10 +1237,9 @@ void GPUContextVulkan::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 of
void GPUContextVulkan::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs) void GPUContextVulkan::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offsetForArgs)
{ {
ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument)); ASSERT(bufferForArgs && EnumHasAnyFlags(bufferForArgs->GetFlags(), GPUBufferFlags::Argument));
OnDrawCall();
auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs; auto bufferForArgsVK = (GPUBufferVulkan*)bufferForArgs;
const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer();
OnDrawCall();
vkCmdDrawIndexedIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndexedIndirectCommand)); vkCmdDrawIndexedIndirect(cmdBuffer->GetHandle(), bufferForArgsVK->GetHandle(), (VkDeviceSize)offsetForArgs, 1, sizeof(VkDrawIndexedIndirectCommand));
RENDER_STAT_DRAW_CALL(0, 0); RENDER_STAT_DRAW_CALL(0, 0);
} }

View File

@@ -1704,6 +1704,7 @@ bool GPUDeviceVulkan::Init()
limits.HasDrawIndirect = PhysicalDeviceLimits.maxDrawIndirectCount >= 1; limits.HasDrawIndirect = PhysicalDeviceLimits.maxDrawIndirectCount >= 1;
limits.HasAppendConsumeBuffers = false; // TODO: add Append Consume buffers support for Vulkan limits.HasAppendConsumeBuffers = false; // TODO: add Append Consume buffers support for Vulkan
limits.HasSeparateRenderTargetBlendState = true; limits.HasSeparateRenderTargetBlendState = true;
limits.HasDepthClip = PhysicalDeviceFeatures.depthClamp;
limits.HasDepthAsSRV = true; limits.HasDepthAsSRV = true;
limits.HasReadOnlyDepth = true; limits.HasReadOnlyDepth = true;
limits.HasMultisampleDepthAsSRV = !!PhysicalDeviceFeatures.sampleRateShading; limits.HasMultisampleDepthAsSRV = !!PhysicalDeviceFeatures.sampleRateShading;

View File

@@ -340,7 +340,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc)
break; break;
} }
_descRasterization.frontFace = VK_FRONT_FACE_CLOCKWISE; _descRasterization.frontFace = VK_FRONT_FACE_CLOCKWISE;
_descRasterization.depthClampEnable = !desc.DepthClipEnable; _descRasterization.depthClampEnable = !desc.DepthClipEnable && _device->Limits.HasDepthClip;
_descRasterization.lineWidth = 1.0f; _descRasterization.lineWidth = 1.0f;
_desc.pRasterizationState = &_descRasterization; _desc.pRasterizationState = &_descRasterization;

View File

@@ -16,7 +16,7 @@ void GPUTimerQueryVulkan::Interrupt(CmdBufferVulkan* cmdBuffer)
if (!_interrupted) if (!_interrupted)
{ {
_interrupted = true; _interrupted = true;
WriteTimestamp(cmdBuffer, _queries[_queryIndex].End); WriteTimestamp(cmdBuffer, _queries[_queryIndex].End, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
} }
} }
@@ -28,7 +28,7 @@ void GPUTimerQueryVulkan::Resume(CmdBufferVulkan* cmdBuffer)
e.End.Pool = nullptr; e.End.Pool = nullptr;
_interrupted = false; _interrupted = false;
WriteTimestamp(cmdBuffer, e.Begin); WriteTimestamp(cmdBuffer, e.Begin, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
_queries.Add(e); _queries.Add(e);
_queryIndex++; _queryIndex++;
@@ -56,13 +56,13 @@ bool GPUTimerQueryVulkan::GetResult(Query& query)
return false; return false;
} }
void GPUTimerQueryVulkan::WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query) const void GPUTimerQueryVulkan::WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const
{ {
auto pool = _device->FindAvailableTimestampQueryPool(); auto pool = _device->FindAvailableTimestampQueryPool();
uint32 index; uint32 index;
pool->AcquireQuery(index); pool->AcquireQuery(index);
vkCmdWriteTimestamp(cmdBuffer->GetHandle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, pool->GetHandle(), index); vkCmdWriteTimestamp(cmdBuffer->GetHandle(), stage, pool->GetHandle(), index);
pool->MarkQueryAsStarted(index); pool->MarkQueryAsStarted(index);
query.Pool = pool; query.Pool = pool;
@@ -168,7 +168,7 @@ void GPUTimerQueryVulkan::Begin()
_queryIndex = 0; _queryIndex = 0;
_interrupted = false; _interrupted = false;
WriteTimestamp(cmdBuffer, e.Begin); WriteTimestamp(cmdBuffer, e.Begin, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
context->GetCmdBufferManager()->OnQueryBegin(this); context->GetCmdBufferManager()->OnQueryBegin(this);
ASSERT(_queries.IsEmpty()); ASSERT(_queries.IsEmpty());
@@ -193,7 +193,7 @@ void GPUTimerQueryVulkan::End()
if (!_interrupted) if (!_interrupted)
{ {
WriteTimestamp(cmdBuffer, _queries[_queryIndex].End); WriteTimestamp(cmdBuffer, _queries[_queryIndex].End, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
} }
context->GetCmdBufferManager()->OnQueryEnd(this); context->GetCmdBufferManager()->OnQueryEnd(this);
} }

View File

@@ -59,7 +59,7 @@ public:
private: private:
bool GetResult(Query& query); bool GetResult(Query& query);
void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query) const; void WriteTimestamp(CmdBufferVulkan* cmdBuffer, Query& query, VkPipelineStageFlagBits stage) const;
bool TryGetResult(); bool TryGetResult();
bool UseQueries(); bool UseQueries();

View File

@@ -258,18 +258,32 @@ void NetworkReplicationService::Dispose()
NetworkReplicationService NetworkReplicationServiceInstance; NetworkReplicationService NetworkReplicationServiceInstance;
void INetworkSerializable_Serialize(void* instance, NetworkStream* stream, void* tag) void INetworkSerializable_Native_Serialize(void* instance, NetworkStream* stream, void* tag)
{ {
const int16 vtableOffset = (int16)(intptr)tag; const int16 vtableOffset = (int16)(intptr)tag;
((INetworkSerializable*)((byte*)instance + vtableOffset))->Serialize(stream); ((INetworkSerializable*)((byte*)instance + vtableOffset))->Serialize(stream);
} }
void INetworkSerializable_Deserialize(void* instance, NetworkStream* stream, void* tag) void INetworkSerializable_Native_Deserialize(void* instance, NetworkStream* stream, void* tag)
{ {
const int16 vtableOffset = (int16)(intptr)tag; const int16 vtableOffset = (int16)(intptr)tag;
((INetworkSerializable*)((byte*)instance + vtableOffset))->Deserialize(stream); ((INetworkSerializable*)((byte*)instance + vtableOffset))->Deserialize(stream);
} }
void INetworkSerializable_Script_Serialize(void* instance, NetworkStream* stream, void* tag)
{
auto obj = (ScriptingObject*)instance;
auto interface = ScriptingObject::ToInterface<INetworkSerializable>(obj);
interface->Serialize(stream);
}
void INetworkSerializable_Script_Deserialize(void* instance, NetworkStream* stream, void* tag)
{
auto obj = (ScriptingObject*)instance;
auto interface = ScriptingObject::ToInterface<INetworkSerializable>(obj);
interface->Deserialize(stream);
}
NetworkReplicatedObject* ResolveObject(Guid objectId) NetworkReplicatedObject* ResolveObject(Guid objectId)
{ {
auto it = Objects.Find(objectId); auto it = Objects.Find(objectId);
@@ -1064,9 +1078,21 @@ bool NetworkReplicator::InvokeSerializer(const ScriptingTypeHandle& typeHandle,
const ScriptingType::InterfaceImplementation* interface = type.GetInterface(INetworkSerializable::TypeInitializer); const ScriptingType::InterfaceImplementation* interface = type.GetInterface(INetworkSerializable::TypeInitializer);
if (interface) if (interface)
{ {
serializer.Methods[0] = INetworkSerializable_Serialize; if (interface->IsNative)
serializer.Methods[1] = INetworkSerializable_Deserialize; {
serializer.Tags[0] = serializer.Tags[1] = (void*)(intptr)interface->VTableOffset; // Pass VTableOffset to the callback // Native interface (implemented in C++)
serializer.Methods[0] = INetworkSerializable_Native_Serialize;
serializer.Methods[1] = INetworkSerializable_Native_Deserialize;
serializer.Tags[0] = serializer.Tags[1] = (void*)(intptr)interface->VTableOffset; // Pass VTableOffset to the callback
}
else
{
// Generic interface (implemented in C# or elsewhere)
ASSERT(type.Type == ScriptingTypes::Script);
serializer.Methods[0] = INetworkSerializable_Script_Serialize;
serializer.Methods[1] = INetworkSerializable_Script_Deserialize;
serializer.Tags[0] = serializer.Tags[1] = nullptr;
}
SerializersTable.Add(typeHandle, serializer); SerializersTable.Add(typeHandle, serializer);
} }
else if (const ScriptingTypeHandle baseTypeHandle = typeHandle.GetType().GetBaseType()) else if (const ScriptingTypeHandle baseTypeHandle = typeHandle.GetType().GetBaseType())

View File

@@ -19,7 +19,7 @@ namespace FlaxEngine
/// <param name="convexFlags">The convex mesh generation flags.</param> /// <param name="convexFlags">The convex mesh generation flags.</param>
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param> /// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public bool CookCollision(CollisionDataType type, Vector3[] vertices, uint[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255) public bool CookCollision(CollisionDataType type, Vector3[] vertices, uint[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
{ {
if (vertices == null) if (vertices == null)
@@ -43,7 +43,7 @@ namespace FlaxEngine
/// <param name="convexFlags">The convex mesh generation flags.</param> /// <param name="convexFlags">The convex mesh generation flags.</param>
/// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param> /// <param name="convexVertexLimit">The convex mesh vertex limit. Use values in range [8;255]</param>
/// <returns>True if failed, otherwise false.</returns> /// <returns>True if failed, otherwise false.</returns>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public bool CookCollision(CollisionDataType type, Vector3[] vertices, int[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255) public bool CookCollision(CollisionDataType type, Vector3[] vertices, int[] triangles, ConvexMeshGenerationFlags convexFlags = ConvexMeshGenerationFlags.None, int convexVertexLimit = 255)
{ {
if (vertices == null) if (vertices == null)
@@ -60,7 +60,7 @@ namespace FlaxEngine
/// </summary> /// </summary>
/// <param name="vertexBuffer">The output vertex buffer.</param> /// <param name="vertexBuffer">The output vertex buffer.</param>
/// <param name="indexBuffer">The output index buffer.</param> /// <param name="indexBuffer">The output index buffer.</param>
[Obsolete("Deprecated in 1.4")] [Obsolete("Deprecated in 1.4, use overload with Float3 and Float2 parameters")]
public void ExtractGeometry(out Vector3[] vertexBuffer, out int[] indexBuffer) public void ExtractGeometry(out Vector3[] vertexBuffer, out int[] indexBuffer)
{ {
ExtractGeometry(out Float3[] tmp, out indexBuffer); ExtractGeometry(out Float3[] tmp, out indexBuffer);

View File

@@ -69,7 +69,30 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView&
} }
FILE* f = popen(cmd, "r"); FILE* f = popen(cmd, "r");
char buf[2048]; char buf[2048];
fgets(buf, ARRAY_COUNT(buf), f); char* writePointer = buf;
int remainingCapacity = ARRAY_COUNT(buf);
// make sure we read all output from kdialog
while (remainingCapacity > 0 && fgets(writePointer, remainingCapacity, f))
{
int r = strlen(writePointer);
writePointer += r;
remainingCapacity -= r;
}
if (remainingCapacity <= 0)
{
LOG(Error, "You selected more files than an internal buffer can hold. Try selecting fewer files at a time.");
// in case of an overflow we miss the closing null byte, add it after the rightmost linefeed
while (*writePointer != '\n')
{
writePointer--;
if (writePointer == buf)
{
*buf = 0;
break;
}
}
*(++writePointer) = 0;
}
int result = pclose(f); int result = pclose(f);
if (result != 0) if (result != 0)
{ {

View File

@@ -108,8 +108,7 @@ WindowsWindow::WindowsWindow(const CreateWindowSettings& settings)
style |= WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_THICKFRAME | WS_GROUP; style |= WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_THICKFRAME | WS_GROUP;
#elif WINDOWS_USE_NEWER_BORDER_LESS #elif WINDOWS_USE_NEWER_BORDER_LESS
if (settings.IsRegularWindow) if (settings.IsRegularWindow)
style |= WS_THICKFRAME | WS_SYSMENU; style |= WS_THICKFRAME | WS_SYSMENU | WS_CAPTION;
style |= WS_CAPTION;
#endif #endif
exStyle |= WS_EX_WINDOWEDGE; exStyle |= WS_EX_WINDOWEDGE;
} }
@@ -220,12 +219,6 @@ void WindowsWindow::Show()
if (!_settings.HasBorder) if (!_settings.HasBorder)
{ {
SetWindowPos(_handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); SetWindowPos(_handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
if (!_settings.IsRegularWindow && _settings.ShowAfterFirstPaint && _settings.StartPosition == WindowStartPosition::Manual)
{
int32 x = Math::TruncToInt(_settings.Position.X);
int32 y = Math::TruncToInt(_settings.Position.Y);
SetWindowPos(_handle, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
}
} }
#endif #endif

View File

@@ -9,6 +9,7 @@
#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
#include "Engine/Graphics/GPUContext.h" #include "Engine/Graphics/GPUContext.h"
#include "Engine/Scripting/Enums.h"
#if USE_EDITOR #if USE_EDITOR
#include "Engine/Renderer/Lightmaps.h" #include "Engine/Renderer/Lightmaps.h"
#endif #endif
@@ -86,11 +87,12 @@ bool ShadowsPass::Init()
const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture); const auto formatFeaturesTexture = GPUDevice::Instance->GetFormatFeatures(formatTexture);
_supportsShadows = EnumHasAllFlags(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D) _supportsShadows = EnumHasAllFlags(formatFeaturesDepth.Support, FormatSupport::DepthStencil | FormatSupport::Texture2D)
&& EnumHasAllFlags(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison); && EnumHasAllFlags(formatFeaturesTexture.Support, FormatSupport::ShaderSample | FormatSupport::ShaderSampleComparison);
// TODO: fallback to 32-bit shadow map format if 16-bit is not supported
if (!_supportsShadows) if (!_supportsShadows)
{ {
LOG(Warning, "GPU doesn't support shadows rendering"); LOG(Warning, "GPU doesn't support shadows rendering");
LOG(Warning, "Format: {0}, features support: {1}", (int32)SHADOW_MAPS_FORMAT, (uint32)formatFeaturesDepth.Support); LOG(Warning, "Format: {0}, features support: {1}", ScriptingEnum::ToString(SHADOW_MAPS_FORMAT), (uint32)formatFeaturesDepth.Support);
LOG(Warning, "Format: {0}, features support: {1}", (int32)formatTexture, (uint32)formatFeaturesTexture.Support); LOG(Warning, "Format: {0}, features support: {1}", ScriptingEnum::ToString(formatTexture), (uint32)formatFeaturesTexture.Support);
} }
return false; return false;

View File

@@ -190,7 +190,6 @@ DEFINE_INTERNAL_CALL(bool) ScriptingInternal_IsTypeFromGameScripts(MTypeObject*
DEFINE_INTERNAL_CALL(void) ScriptingInternal_FlushRemovedObjects() DEFINE_INTERNAL_CALL(void) ScriptingInternal_FlushRemovedObjects()
{ {
ASSERT(IsInMainThread());
ObjectsRemovalService::Flush(); ObjectsRemovalService::Flush();
} }

View File

@@ -1,5 +1,8 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "ManagedDictionary.h" #include "ManagedDictionary.h"
#if USE_CSHARP
Dictionary<ManagedDictionary::KeyValueType, MTypeObject*> ManagedDictionary::CachedDictionaryTypes; Dictionary<ManagedDictionary::KeyValueType, MTypeObject*> ManagedDictionary::CachedDictionaryTypes;
#if !USE_MONO_AOT #if !USE_MONO_AOT
ManagedDictionary::MakeGenericTypeThunk ManagedDictionary::MakeGenericType; ManagedDictionary::MakeGenericTypeThunk ManagedDictionary::MakeGenericType;
@@ -12,3 +15,4 @@ MMethod* ManagedDictionary::CreateInstance;
MMethod* ManagedDictionary::AddDictionaryItem; MMethod* ManagedDictionary::AddDictionaryItem;
MMethod* ManagedDictionary::GetDictionaryKeys; MMethod* ManagedDictionary::GetDictionaryKeys;
#endif #endif
#endif

View File

@@ -25,8 +25,8 @@ private:
MonoAssembly* _monoAssembly = nullptr; MonoAssembly* _monoAssembly = nullptr;
MonoImage* _monoImage = nullptr; MonoImage* _monoImage = nullptr;
#elif USE_NETCORE #elif USE_NETCORE
StringAnsi _fullname;
void* _handle = nullptr; void* _handle = nullptr;
StringAnsi _fullname;
#endif #endif
MDomain* _domain; MDomain* _domain;
@@ -50,6 +50,7 @@ public:
/// <param name="name">The assembly name.</param> /// <param name="name">The assembly name.</param>
MAssembly(MDomain* domain, const StringAnsiView& name); MAssembly(MDomain* domain, const StringAnsiView& name);
#if USE_NETCORE
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="MAssembly"/> class. /// Initializes a new instance of the <see cref="MAssembly"/> class.
/// </summary> /// </summary>
@@ -58,6 +59,7 @@ public:
/// <param name="fullname">The assembly full name.</param> /// <param name="fullname">The assembly full name.</param>
/// <param name="handle">The managed handle of the assembly.</param> /// <param name="handle">The managed handle of the assembly.</param>
MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle); MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle);
#endif
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="MAssembly"/> class. /// Finalizes an instance of the <see cref="MAssembly"/> class.

View File

@@ -48,18 +48,22 @@ MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name)
{ {
} }
#if USE_NETCORE
MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle) MAssembly::MAssembly(MDomain* domain, const StringAnsiView& name, const StringAnsiView& fullname, void* handle)
: _domain(domain) : _handle(handle)
, _fullname(fullname)
, _domain(domain)
, _isLoaded(false) , _isLoaded(false)
, _isLoading(false) , _isLoading(false)
, _hasCachedClasses(false) , _hasCachedClasses(false)
, _reloadCount(0) , _reloadCount(0)
, _name(name) , _name(name)
, _fullname(fullname)
, _handle(handle)
{ {
} }
#endif
MAssembly::~MAssembly() MAssembly::~MAssembly()
{ {
Unload(); Unload();

View File

@@ -41,12 +41,18 @@
#include <mono/jit/mono-private-unstable.h> #include <mono/jit/mono-private-unstable.h>
#include <mono/utils/mono-logger.h> #include <mono/utils/mono-logger.h>
#include <mono/metadata/assembly.h> #include <mono/metadata/assembly.h>
#include <mono/metadata/appdomain.h>
#include <mono/metadata/class.h> #include <mono/metadata/class.h>
#include <mono/metadata/metadata.h> #include <mono/metadata/metadata.h>
#include <mono/metadata/threads.h>
#include <mono/metadata/reflection.h> #include <mono/metadata/reflection.h>
#include <mono/metadata/mono-private-unstable.h> #include <mono/metadata/mono-private-unstable.h>
typedef char char_t; typedef char char_t;
#define DOTNET_HOST_MONO_DEBUG 0 #define DOTNET_HOST_MONO_DEBUG 0
#ifdef USE_MONO_AOT_MODULE
void* MonoAotModuleHandle = nullptr;
#endif
MonoDomain* MonoDomainHandle = nullptr;
#else #else
#error "Unknown .NET runtime host." #error "Unknown .NET runtime host."
#endif #endif
@@ -180,7 +186,7 @@ void* GetStaticMethodPointer(const String& methodName);
/// Calls the managed static method in NativeInterop class with given parameters. /// Calls the managed static method in NativeInterop class with given parameters.
/// </summary> /// </summary>
template<typename RetType, typename... Args> template<typename RetType, typename... Args>
inline RetType CallStaticMethodByName(const String& methodName, Args... args) FORCE_INLINE RetType CallStaticMethodByName(const String& methodName, Args... args)
{ {
typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...); typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
return ((fun)GetStaticMethodPointer(methodName))(args...); return ((fun)GetStaticMethodPointer(methodName))(args...);
@@ -190,7 +196,7 @@ inline RetType CallStaticMethodByName(const String& methodName, Args... args)
/// Calls the managed static method with given parameters. /// Calls the managed static method with given parameters.
/// </summary> /// </summary>
template<typename RetType, typename... Args> template<typename RetType, typename... Args>
inline RetType CallStaticMethod(void* methodPtr, Args... args) FORCE_INLINE RetType CallStaticMethod(void* methodPtr, Args... args)
{ {
typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...); typedef RetType (CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
return ((fun)methodPtr)(args...); return ((fun)methodPtr)(args...);
@@ -516,6 +522,12 @@ void MCore::GC::FreeMemory(void* ptr, bool coTaskMem)
void MCore::Thread::Attach() void MCore::Thread::Attach()
{ {
#if DOTNET_HOST_MONO
if (!IsInMainThread() && !mono_domain_get())
{
mono_thread_attach(MonoDomainHandle);
}
#endif
} }
void MCore::Thread::Exit() void MCore::Thread::Exit()
@@ -617,14 +629,33 @@ bool MCore::Type::IsReference(MType* type)
void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id) void MCore::ScriptingObject::SetInternalValues(MClass* klass, MObject* object, void* unmanagedPtr, const Guid* id)
{ {
#if PLATFORM_DESKTOP && !USE_MONO_AOT
static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectSetInternalValues")); static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectSetInternalValues"));
CallStaticMethod<void, MObject*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id); CallStaticMethod<void, MObject*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, object, unmanagedPtr, id);
#else
const MField* monoUnmanagedPtrField = klass->GetField("__unmanagedPtr");
if (monoUnmanagedPtrField)
monoUnmanagedPtrField->SetValue(object, &unmanagedPtr);
const MField* monoIdField = klass->GetField("__internalId");
if (id != nullptr && monoIdField)
monoIdField->SetValue(object, (void*)id);
#endif
} }
MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id) MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unmanagedPtr, const Guid* id)
{ {
#if PLATFORM_DESKTOP && !USE_MONO_AOT
static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectCreate")); static void* ScriptingObjectSetInternalValuesPtr = GetStaticMethodPointer(TEXT("ScriptingObjectCreate"));
return CallStaticMethod<MObject*, void*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, klass->_handle, unmanagedPtr, id); return CallStaticMethod<MObject*, void*, void*, const Guid*>(ScriptingObjectSetInternalValuesPtr, klass->_handle, unmanagedPtr, id);
#else
MObject* object = MCore::Object::New(klass);
if (object)
{
MCore::ScriptingObject::SetInternalValues(klass, object, unmanagedPtr, id);
MCore::Object::Init(object);
}
return object;
#endif
} }
const MAssembly::ClassesDictionary& MAssembly::GetClasses() const const MAssembly::ClassesDictionary& MAssembly::GetClasses() const
@@ -1241,8 +1272,8 @@ void MField::GetValue(MObject* instance, void* result) const
void MField::GetValueReference(MObject* instance, void* result) const void MField::GetValueReference(MObject* instance, void* result) const
{ {
static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReferenceWithOffset")); static void* FieldGetValueReferencePtr = GetStaticMethodPointer(TEXT("FieldGetValueReference"));
CallStaticMethod<void, void*, int, void*>(FieldGetValueReferencePtr, instance, _fieldOffset, result); CallStaticMethod<void, void*, void*, int, void*>(FieldGetValueReferencePtr, instance, _handle, _fieldOffset, result);
} }
MObject* MField::GetValueBoxed(MObject* instance) const MObject* MField::GetValueBoxed(MObject* instance) const
@@ -1767,11 +1798,6 @@ void* GetStaticMethodPointer(const String& methodName)
#elif DOTNET_HOST_MONO #elif DOTNET_HOST_MONO
#ifdef USE_MONO_AOT_MODULE
void* MonoAotModuleHandle = nullptr;
#endif
MonoDomain* MonoDomainHandle = nullptr;
void OnLogCallback(const char* logDomain, const char* logLevel, const char* message, mono_bool fatal, void* userData) void OnLogCallback(const char* logDomain, const char* logLevel, const char* message, mono_bool fatal, void* userData)
{ {
String currentDomain(logDomain); String currentDomain(logDomain);

View File

@@ -2157,7 +2157,7 @@ MObject* MCore::ScriptingObject::CreateScriptingObject(MClass* klass, void* unma
if (managedInstance) if (managedInstance)
{ {
// Set unmanaged object handle and id // Set unmanaged object handle and id
MCore::ScriptingObject::SetInternalValues(klass, managedInstance, unmanagedPtr, _id); MCore::ScriptingObject::SetInternalValues(klass, managedInstance, unmanagedPtr, id);
// Initialize managed instance (calls constructor) // Initialize managed instance (calls constructor)
MCore::Object::Init(managedInstance); MCore::Object::Init(managedInstance);

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "Engine/Scripting/Types.h" #include "Engine/Scripting/Types.h"
#if !USE_CSHARP #if !USE_CSHARP
#include "Engine/Core/Types/Span.h"
#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ManagedCLR/MCore.h"
#include "Engine/Scripting/ManagedCLR/MDomain.h" #include "Engine/Scripting/ManagedCLR/MDomain.h"
#include "Engine/Scripting/ManagedCLR/MAssembly.h" #include "Engine/Scripting/ManagedCLR/MAssembly.h"

View File

@@ -493,9 +493,11 @@ bool Scripting::Load()
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"]; flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float3"];
flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"]; flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Float4"];
#endif #endif
#if USE_CSHARP
flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"]; flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector2")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector2"];
flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"]; flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector3")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector3"];
flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"]; flaxEngineModule->ClassToTypeIndex[flaxEngineModule->Assembly->GetClass("FlaxEngine.Vector4")] = flaxEngineModule->TypeNameToTypeIndex["FlaxEngine.Vector4"];
#endif
#if USE_EDITOR #if USE_EDITOR
// Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation) // Skip loading game modules in Editor on startup - Editor loads them later during splash screen (eg. after first compilation)

View File

@@ -6,6 +6,7 @@
#include "BinaryModule.h" #include "BinaryModule.h"
#include "Engine/Level/Actor.h" #include "Engine/Level/Actor.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Utilities/StringConverter.h" #include "Engine/Utilities/StringConverter.h"
#include "Engine/Content/Asset.h" #include "Engine/Content/Asset.h"
#include "Engine/Content/Content.h" #include "Engine/Content/Content.h"
@@ -25,7 +26,8 @@
#define ScriptingObject_id "__internalId" #define ScriptingObject_id "__internalId"
// TODO: don't leak memory (use some kind of late manual GC for those wrapper objects) // TODO: don't leak memory (use some kind of late manual GC for those wrapper objects)
Dictionary<ScriptingObject*, void*> ScriptingObjectsInterfaceWrappers; typedef Pair<ScriptingObject*, ScriptingTypeHandle> ScriptingObjectsInterfaceKey;
Dictionary<ScriptingObjectsInterfaceKey, void*> ScriptingObjectsInterfaceWrappers;
SerializableScriptingObject::SerializableScriptingObject(const SpawnParams& params) SerializableScriptingObject::SerializableScriptingObject(const SpawnParams& params)
: ScriptingObject(params) : ScriptingObject(params)
@@ -202,10 +204,10 @@ ScriptingObject* ScriptingObject::FromInterface(void* interfaceObj, const Script
} }
// Special case for interface wrapper object // Special case for interface wrapper object
for (auto& e : ScriptingObjectsInterfaceWrappers) for (const auto& e : ScriptingObjectsInterfaceWrappers)
{ {
if (e.Value == interfaceObj) if (e.Value == interfaceObj)
return e.Key; return e.Key.First;
} }
return nullptr; return nullptr;
@@ -226,10 +228,11 @@ void* ScriptingObject::ToInterface(ScriptingObject* obj, const ScriptingTypeHand
else if (interface) else if (interface)
{ {
// Interface implemented in scripting (eg. C# class inherits C++ interface) // Interface implemented in scripting (eg. C# class inherits C++ interface)
if (!ScriptingObjectsInterfaceWrappers.TryGet(obj, result)) const ScriptingObjectsInterfaceKey key(obj, interfaceType);
if (!ScriptingObjectsInterfaceWrappers.TryGet(key, result))
{ {
result = interfaceType.GetType().Interface.GetInterfaceWrapper(obj); result = interfaceType.GetType().Interface.GetInterfaceWrapper(obj);
ScriptingObjectsInterfaceWrappers.Add(obj, result); ScriptingObjectsInterfaceWrappers.Add(key, result);
} }
} }
return result; return result;
@@ -241,7 +244,7 @@ ScriptingObject* ScriptingObject::ToNative(MObject* obj)
#if USE_CSHARP #if USE_CSHARP
if (obj) if (obj)
{ {
#if USE_MONO #if USE_MONO || USE_MONO_AOT
const auto ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr); const auto ptrField = MCore::Object::GetClass(obj)->GetField(ScriptingObject_unmanagedPtr);
CHECK_RETURN(ptrField, nullptr); CHECK_RETURN(ptrField, nullptr);
ptrField->GetValue(obj, &ptr); ptrField->GetValue(obj, &ptr);

View File

@@ -113,7 +113,7 @@ void SpriteRender::Draw(RenderContext& renderContext)
auto model = _quadModel.As<Model>(); auto model = _quadModel.As<Model>();
if (model->GetLoadedLODs() == 0) if (model->GetLoadedLODs() == 0)
return; return;
const auto& view = renderContext.View; const auto& view = (renderContext.LodProxyView ? *renderContext.LodProxyView : renderContext.View);
Matrix m1, m2, m3, world; Matrix m1, m2, m3, world;
Matrix::Scaling(_size.X, _size.Y, 1.0f, m2); Matrix::Scaling(_size.X, _size.Y, 1.0f, m2);
Matrix::RotationY(PI, m3); Matrix::RotationY(PI, m3);

View File

@@ -56,6 +56,7 @@ UICanvas::UICanvas(const SpawnParams& params)
UICanvas_EndPlay = mclass->GetMethod("EndPlay"); UICanvas_EndPlay = mclass->GetMethod("EndPlay");
UICanvas_ParentChanged = mclass->GetMethod("ParentChanged"); UICanvas_ParentChanged = mclass->GetMethod("ParentChanged");
UICanvas_Serialize = mclass->GetMethod("Serialize"); UICanvas_Serialize = mclass->GetMethod("Serialize");
Platform::MemoryBarrier();
} }
#endif #endif
} }