Merge remote-tracking branch 'origin/master' into 1.12
This commit is contained in:
@@ -31,7 +31,7 @@ body:
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- master branch
|
||||
default: 2
|
||||
default: 3
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
+1
-1
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6801
|
||||
"Build": 6804
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Fedora: `sudo dnf install dotnet-sdk-8.0`
|
||||
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||
* Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs)
|
||||
* Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Install Git with LFS
|
||||
@@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Install Clang compiler (version 14 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
* Arch: `sudo pacman -S clang lldb lld`
|
||||
|
||||
@@ -130,6 +130,11 @@ namespace FlaxEditor.Content
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
|
||||
var antiAliasing = preview.PostFxVolume.AntiAliasing;
|
||||
antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing;
|
||||
antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode;
|
||||
preview.PostFxVolume.AntiAliasing = antiAliasing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,26 +15,32 @@
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
String GetGDK()
|
||||
{
|
||||
// Find GDK
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
String gdk;
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
{
|
||||
_gdkPath.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
gdk.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
{
|
||||
_gdkPath.Clear();
|
||||
gdk.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_gdkPath.EndsWith(TEXT("GRDK\\")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 6);
|
||||
else if (_gdkPath.EndsWith(TEXT("GRDK")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 5);
|
||||
if (gdk.EndsWith(TEXT("GRDK\\")))
|
||||
gdk.Remove(gdk.Length() - 6);
|
||||
else if (gdk.EndsWith(TEXT("GRDK")))
|
||||
gdk.Remove(gdk.Length() - 5);
|
||||
}
|
||||
}
|
||||
return gdk;
|
||||
}
|
||||
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
{
|
||||
_gdkPath = GetGDK();
|
||||
}
|
||||
|
||||
DotNetAOTModes GDKPlatformTools::UseAOT() const
|
||||
@@ -121,7 +127,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
|
||||
validName.Add('\0');
|
||||
|
||||
sb.Append(TEXT("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"0\">\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"1\">\n"));
|
||||
sb.AppendFormat(TEXT(" <Identity Name=\"{0}\" Publisher=\"{1}\" Version=\"{2}\"/>\n"),
|
||||
validName.Get(),
|
||||
platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName,
|
||||
|
||||
@@ -526,6 +526,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
case BuildPlatform::XboxScarlett:
|
||||
{
|
||||
options.Platform = PlatformType::XboxScarlett;
|
||||
const char* platformDefineName = "PLATFORM_XBOX_SCARLETT";
|
||||
COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE);
|
||||
break;
|
||||
|
||||
@@ -265,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (version.IsEmpty())
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer));
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(EnvironmentProbe)), DefaultEditor]
|
||||
public class EnvironmentProbeEditor : ActorEditor
|
||||
{
|
||||
private FlaxEngine.GUI.Button _bake;
|
||||
private Button _bake;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -20,8 +21,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
layout.Space(10);
|
||||
_bake = layout.Button("Bake").Button;
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
_bake = group.Button("Bake").Button;
|
||||
_bake.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,9 +914,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Remove drop down arrows and containment lines if no objects in the group
|
||||
if (group.Children.Count == 0)
|
||||
{
|
||||
group.Panel.Close();
|
||||
group.Panel.ArrowImageOpened = null;
|
||||
group.Panel.ArrowImageClosed = null;
|
||||
group.Panel.EnableContainmentLines = false;
|
||||
group.Panel.CanOpenClose = false;
|
||||
}
|
||||
|
||||
// Scripts arrange bar
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -19,8 +20,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
// Add 'Bake' button
|
||||
layout.Space(10);
|
||||
var button = layout.Button("Bake");
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
var button = group.Button("Bake");
|
||||
button.Button.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (Picker == null)
|
||||
return;
|
||||
var differentValues = HasDifferentValues;
|
||||
Picker.DifferentValues = differentValues;
|
||||
if (!differentValues)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
panel.Panel.Size = new Float2(0, 18);
|
||||
panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0);
|
||||
|
||||
var removeButton = panel.Button("-", "Remove the last item");
|
||||
var removeButton = panel.Button("-", "Remove the last item.");
|
||||
removeButton.Button.Size = new Float2(16, 16);
|
||||
removeButton.Button.Enabled = size > _minCount;
|
||||
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
@@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
Resize(Count - 1);
|
||||
};
|
||||
|
||||
var addButton = panel.Button("+", "Add a new item");
|
||||
var addButton = panel.Button("+", "Add a new item.");
|
||||
addButton.Button.Size = new Float2(16, 16);
|
||||
addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount;
|
||||
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public event Action<TypePickerControl> TypePickerValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for types validation. Cane be used to implement a rule for types to pick.
|
||||
/// The custom callback for types validation. Can be used to implement a rule for types to pick.
|
||||
/// </summary>
|
||||
public Func<ScriptType, bool> CheckValid;
|
||||
|
||||
@@ -353,7 +353,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
if (!string.IsNullOrEmpty(typeReference.CheckMethod))
|
||||
{
|
||||
var parentType = ParentEditor.Values[0].GetType();
|
||||
var parentEditor = ParentEditor;
|
||||
// Find actual parent editor if parent editor is collection editor
|
||||
while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor)))
|
||||
parentEditor = parentEditor.ParentEditor;
|
||||
|
||||
var parentType = parentEditor.Values[0].GetType();
|
||||
|
||||
var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (method != null)
|
||||
{
|
||||
|
||||
@@ -268,8 +268,8 @@ bool Editor::CheckProjectUpgrade()
|
||||
// Check if last version was older
|
||||
else if (lastVersion.Major < FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor < FLAXENGINE_VERSION_MINOR))
|
||||
{
|
||||
LOG(Warning, "The project was opened with the older editor version last time");
|
||||
const auto result = MessageBox::Show(TEXT("The project was opened with the older editor version last time. Loading it may modify existing data so older editor version won't open it. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
|
||||
LOG(Warning, "The project was last opened with an older editor version");
|
||||
const auto result = MessageBox::Show(TEXT("The project was last opened with an older editor version.\nLoading it may modify existing data, which can result in older editor versions being unable to open it.\n\nDo you want to perform a backup before or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
|
||||
if (result == DialogResult::Yes)
|
||||
{
|
||||
if (BackupProject())
|
||||
@@ -291,8 +291,8 @@ bool Editor::CheckProjectUpgrade()
|
||||
// Check if last version was newer
|
||||
else if (lastVersion.Major > FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor > FLAXENGINE_VERSION_MINOR))
|
||||
{
|
||||
LOG(Warning, "The project was opened with the newer editor version last time");
|
||||
const auto result = MessageBox::Show(TEXT("The project was opened with the newer editor version last time. Loading it may fail and corrupt existing data. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "The project was last opened with a newer editor version");
|
||||
const auto result = MessageBox::Show(TEXT("The project was last opened with a newer editor version.\nLoading it may fail and corrupt existing data.\n\nDo you want to perform a backup before loading or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
|
||||
if (result == DialogResult::Yes)
|
||||
{
|
||||
if (BackupProject())
|
||||
|
||||
+14
-3
@@ -1390,6 +1390,7 @@ namespace FlaxEditor
|
||||
public void BuildAllMeshesSDF()
|
||||
{
|
||||
var models = new List<Model>();
|
||||
var forceRebuild = Input.GetKey(KeyboardKeys.F);
|
||||
Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
|
||||
@@ -1399,7 +1400,7 @@ namespace FlaxEditor
|
||||
model != null &&
|
||||
!models.Contains(model) &&
|
||||
!model.IsVirtual &&
|
||||
model.SDF.Texture == null)
|
||||
(forceRebuild || model.SDF.Texture == null))
|
||||
{
|
||||
models.Add(model);
|
||||
}
|
||||
@@ -1412,7 +1413,17 @@ namespace FlaxEditor
|
||||
{
|
||||
var model = models[i];
|
||||
Log($"[{i}/{models.Count}] Generating SDF for {model}");
|
||||
if (!model.GenerateSDF())
|
||||
float resolutionScale = 1.0f, backfacesThreshold = 0.6f;
|
||||
int lodIndex = 6;
|
||||
bool useGPU = true;
|
||||
var sdf = model.SDF;
|
||||
if (sdf.Texture != null)
|
||||
{
|
||||
// Preserve options set on this model
|
||||
resolutionScale = sdf.ResolutionScale;
|
||||
lodIndex = sdf.LOD;
|
||||
}
|
||||
if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU))
|
||||
model.Save();
|
||||
}
|
||||
});
|
||||
@@ -1587,7 +1598,7 @@ namespace FlaxEditor
|
||||
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
|
||||
result = dockedTo.SelectedTab.Size;
|
||||
else
|
||||
result = gameWin.Viewport.Size;
|
||||
result = gameWin.Viewport.ContentSize;
|
||||
|
||||
result *= root.DpiScale;
|
||||
result = Float2.Round(result);
|
||||
|
||||
@@ -129,11 +129,39 @@ namespace FlaxEditor.GUI.Input
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
var style = Style.Current;
|
||||
var r = new Rectangle(0, 0, Width, Height);
|
||||
bool isTransparent = _value.A < 1;
|
||||
|
||||
Render2D.FillRectangle(r, _value);
|
||||
Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
var style = Style.Current;
|
||||
var fullRect = new Rectangle(0, 0, Width, Height);
|
||||
var colorRect = new Rectangle(0, 0, isTransparent ? Width * 0.7f : Width, Height);
|
||||
|
||||
if (isTransparent)
|
||||
{
|
||||
var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height);
|
||||
|
||||
// Draw checkerboard pattern to part of the color value box
|
||||
Render2D.FillRectangle(alphaRect, Color.White);
|
||||
var smallRectSize = 7.9f;
|
||||
var numHor = Mathf.CeilToInt(alphaRect.Width / smallRectSize);
|
||||
var numVer = Mathf.CeilToInt(alphaRect.Height / smallRectSize);
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0)
|
||||
{
|
||||
var rect = new Rectangle(alphaRect.X + smallRectSize * i, alphaRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.PushClip(alphaRect);
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.FillRectangle(alphaRect, _value);
|
||||
}
|
||||
|
||||
Render2D.FillRectangle(colorRect, _value with { A = 1 });
|
||||
Render2D.DrawRectangle(fullRect, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -447,8 +447,8 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Select previous parent child
|
||||
var select = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
|
||||
// Select last child if is valid and expanded and has any children
|
||||
if (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
// Get bottom most child node
|
||||
while (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
{
|
||||
select = select.GetChild(select.ChildrenCount - 1) as TreeNode;
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ namespace FlaxEditor.Modules
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
@@ -714,6 +714,7 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG);
|
||||
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh);
|
||||
_menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF);
|
||||
_menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one.");
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
|
||||
_menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel());
|
||||
|
||||
@@ -650,6 +650,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Windows"), EditorOrder(4020)]
|
||||
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Control+Comma")]
|
||||
[EditorDisplay("Windows"), EditorOrder(4030)]
|
||||
public InputBinding EditorOptionsWindow = new InputBinding(KeyboardKeys.Comma, KeyboardKeys.Control);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Node Editors
|
||||
|
||||
@@ -726,7 +726,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
private void OnSurfaceMouseUp(ref Float2 mouse, MouseButton buttons, ref bool handled)
|
||||
{
|
||||
if (handled)
|
||||
if (handled || Surface.Context != Context)
|
||||
return;
|
||||
|
||||
// Check click over the connection
|
||||
@@ -751,7 +751,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
private void OnSurfaceMouseDoubleClick(ref Float2 mouse, MouseButton buttons, ref bool handled)
|
||||
{
|
||||
if (handled)
|
||||
if (handled || Surface.Context != Context)
|
||||
return;
|
||||
|
||||
// Check double click over the connection
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
@@ -18,6 +15,7 @@ namespace FlaxEditor.Surface
|
||||
class AttributesEditor : ContextMenuBase
|
||||
{
|
||||
private CustomEditorPresenter _presenter;
|
||||
private Proxy _proxy;
|
||||
private byte[] _oldData;
|
||||
|
||||
private class Proxy
|
||||
@@ -72,11 +70,11 @@ namespace FlaxEditor.Surface
|
||||
/// Initializes a new instance of the <see cref="AttributesEditor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The attributes list to edit.</param>
|
||||
/// <param name="attributeType">The allowed attribute types to use.</param>
|
||||
public AttributesEditor(Attribute[] attributes, IList<Type> attributeType)
|
||||
/// <param name="attributeTypes">The allowed attribute types to use.</param>
|
||||
public AttributesEditor(Attribute[] attributes, IList<Type> attributeTypes)
|
||||
{
|
||||
// Context menu dimensions
|
||||
const float width = 340.0f;
|
||||
const float width = 375.0f;
|
||||
const float height = 370.0f;
|
||||
Size = new Float2(width, height);
|
||||
|
||||
@@ -88,61 +86,68 @@ namespace FlaxEditor.Surface
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Buttons
|
||||
float buttonsWidth = (width - 16.0f) * 0.5f;
|
||||
// Ok and Cancel Buttons
|
||||
float buttonsWidth = (width - 12.0f) * 0.5f;
|
||||
float buttonsHeight = 20.0f;
|
||||
var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight)
|
||||
var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "Ok",
|
||||
Parent = this
|
||||
};
|
||||
okButton.Clicked += OnOkButtonClicked;
|
||||
var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "Cancel",
|
||||
Parent = this
|
||||
};
|
||||
cancelButton.Clicked += Hide;
|
||||
var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "OK",
|
||||
Parent = this
|
||||
};
|
||||
okButton.Clicked += OnOkButtonClicked;
|
||||
|
||||
// Actual panel
|
||||
// Actual panel used to display attributes
|
||||
var panel1 = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f),
|
||||
Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f),
|
||||
Parent = this
|
||||
};
|
||||
var editor = new CustomEditorPresenter(null);
|
||||
editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
editor.Panel.IsScrollable = true;
|
||||
editor.Panel.Parent = panel1;
|
||||
editor.Panel.Tag = attributeType;
|
||||
editor.Panel.Tag = attributeTypes;
|
||||
_presenter = editor;
|
||||
|
||||
// Cache 'previous' state to check if attributes were edited after operation
|
||||
_oldData = SurfaceMeta.GetAttributesData(attributes);
|
||||
|
||||
editor.Select(new Proxy
|
||||
_proxy = new Proxy
|
||||
{
|
||||
Value = attributes,
|
||||
});
|
||||
};
|
||||
editor.Select(_proxy);
|
||||
|
||||
_presenter.Modified += OnPresenterModified;
|
||||
OnPresenterModified();
|
||||
}
|
||||
|
||||
private void OnPresenterModified()
|
||||
{
|
||||
if (_proxy.Value.Length == 0)
|
||||
{
|
||||
var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center);
|
||||
label.Label.Wrapping = TextWrapping.WrapWords;
|
||||
label.Control.Height = 35f;
|
||||
label.Label.Margin = new Margin(10f);
|
||||
label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOkButtonClicked()
|
||||
{
|
||||
var newValue = ((Proxy)_presenter.Selection[0]).Value;
|
||||
for (int i = 0; i < newValue.Length; i++)
|
||||
{
|
||||
if (newValue[i] == null)
|
||||
{
|
||||
MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
newValue = newValue.Where(v => v != null).ToArray();
|
||||
|
||||
var newData = SurfaceMeta.GetAttributesData(newValue);
|
||||
if (!_oldData.SequenceEqual(newData))
|
||||
{
|
||||
Edited?.Invoke(newValue);
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
@@ -183,7 +188,9 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
_presenter = null;
|
||||
_oldData = null;
|
||||
_proxy = null;
|
||||
Edited = null;
|
||||
_presenter.Modified -= OnPresenterModified;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -214,22 +214,25 @@ namespace FlaxEditor.Surface
|
||||
if (!_isRenaming)
|
||||
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||
|
||||
// Close button
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Color button
|
||||
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if is resizing
|
||||
if (_isResizing)
|
||||
if (Surface.CanEdit)
|
||||
{
|
||||
// Draw overlay
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
// Close button
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Resize button
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
// Color button
|
||||
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if is resizing
|
||||
if (_isResizing)
|
||||
{
|
||||
// Draw overlay
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
|
||||
// Resize button
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
// Selection outline
|
||||
if (_isSelected)
|
||||
|
||||
@@ -431,27 +431,6 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public bool HasIndependentBoxes => Archetype.IndependentBoxes != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned.
|
||||
/// </summary>
|
||||
public bool HasDependentBoxesSetup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < Archetype.DependentBoxes.Length; i++)
|
||||
{
|
||||
var b = GetBox(Archetype.DependentBoxes[i]);
|
||||
if (b != null && b.CurrentType == b.DefaultType)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<SurfaceNode> UpdateStack = new List<SurfaceNode>();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -469,7 +469,8 @@ namespace FlaxEditor.Surface
|
||||
bool handled = base.OnMouseDown(location, button);
|
||||
if (!handled)
|
||||
CustomMouseDown?.Invoke(ref location, button, ref handled);
|
||||
if (handled)
|
||||
var root = Root;
|
||||
if (handled || root == null)
|
||||
{
|
||||
// Clear flags
|
||||
_isMovingSelection = false;
|
||||
@@ -523,11 +524,11 @@ namespace FlaxEditor.Surface
|
||||
if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation))
|
||||
{
|
||||
// Check if user is pressing control
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
if (root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
AddToSelection(controlUnderMouse);
|
||||
}
|
||||
else if (Root.GetKey(KeyboardKeys.Shift))
|
||||
else if (root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
RemoveFromSelection(controlUnderMouse);
|
||||
}
|
||||
@@ -539,7 +540,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Start moving selected nodes
|
||||
if (!Root.GetKey(KeyboardKeys.Shift))
|
||||
if (!root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
StartMouseCapture();
|
||||
_movingSelectionViewPos = _rootControl.Location;
|
||||
@@ -559,7 +560,7 @@ namespace FlaxEditor.Surface
|
||||
// Start selecting or commenting
|
||||
StartMouseCapture();
|
||||
|
||||
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
|
||||
if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
@@ -178,19 +178,31 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Update boxes types for nodes that dependant box types based on incoming connections
|
||||
{
|
||||
bool keepUpdating = false;
|
||||
int updateLimit = 100;
|
||||
bool keepUpdating = true;
|
||||
int updatesMin = 2, updatesMax = 100;
|
||||
do
|
||||
{
|
||||
keepUpdating = false;
|
||||
for (int i = 0; i < RootControl.Children.Count; i++)
|
||||
{
|
||||
if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup)
|
||||
if (RootControl.Children[i] is SurfaceNode node)
|
||||
{
|
||||
node.UpdateBoxesTypes();
|
||||
keepUpdating = true;
|
||||
var arch = node.Archetype;
|
||||
if (arch.DependentBoxes != null && arch.IndependentBoxes != null)
|
||||
{
|
||||
foreach (var boxId in arch.DependentBoxes)
|
||||
{
|
||||
var b = node.GetBox(boxId);
|
||||
if (b != null && b.CurrentType == b.DefaultType)
|
||||
{
|
||||
keepUpdating = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (keepUpdating && updateLimit-- > 0);
|
||||
} while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0);
|
||||
}
|
||||
|
||||
Loaded?.Invoke(this);
|
||||
|
||||
@@ -74,11 +74,6 @@ struct TextureDataResult
|
||||
PixelFormat Format;
|
||||
Int2 Mip0Size;
|
||||
BytesContainer* Mip0DataPtr;
|
||||
|
||||
TextureDataResult()
|
||||
: Lock(FlaxStorage::LockData::Invalid)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Viewport
|
||||
private PrefabUIEditorRoot _uiRoot;
|
||||
private bool _showUI = false;
|
||||
|
||||
private int _defaultScaleActiveIndex = 0;
|
||||
private int _defaultScaleActiveIndex = -1;
|
||||
private int _customScaleActiveIndex = -1;
|
||||
private ContextMenuButton _uiModeButton;
|
||||
private ContextMenuChildMenu _uiViewOptions;
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
|
||||
private string _cacheFolder;
|
||||
private Guid _assetId;
|
||||
private AssetItem _item;
|
||||
private Surface _surface;
|
||||
private Label _loadingLabel;
|
||||
private CancellationTokenSource _token;
|
||||
@@ -163,13 +163,13 @@ namespace FlaxEditor.Windows
|
||||
public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem)
|
||||
: base(editor, false, ScrollBars.None)
|
||||
{
|
||||
Title = assetItem.ShortName + " References";
|
||||
_item = assetItem;
|
||||
Title = _item.ShortName + " References";
|
||||
|
||||
_tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder));
|
||||
_cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References");
|
||||
if (!Directory.Exists(_cacheFolder))
|
||||
Directory.CreateDirectory(_cacheFolder);
|
||||
_assetId = assetItem.ID;
|
||||
_surface = new Surface(this)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
@@ -194,6 +194,7 @@ namespace FlaxEditor.Windows
|
||||
_nodesAssets.Add(assetId);
|
||||
var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId);
|
||||
_nodes.Add(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -392,8 +393,7 @@ namespace FlaxEditor.Windows
|
||||
_nodesAssets = new HashSet<Guid>();
|
||||
var searchLevel = 4; // TODO: make it as an option (somewhere in window UI)
|
||||
// TODO: add option to filter assets by type (eg. show only textures as leaf nodes)
|
||||
var assetNode = SpawnNode(_assetId);
|
||||
// TODO: add some outline or tint color to the main node
|
||||
var assetNode = SpawnNode(_item.ID);
|
||||
BuildGraph(assetNode, searchLevel, false);
|
||||
ArrangeGraph(assetNode, false);
|
||||
BuildGraph(assetNode, searchLevel, true);
|
||||
@@ -402,6 +402,10 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
_progress = 100.0f;
|
||||
|
||||
var commentRect = assetNode.EditorBounds;
|
||||
commentRect.Expand(80f);
|
||||
_surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green);
|
||||
|
||||
// Update UI
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ using FlaxEditor.Surface;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
@@ -430,7 +429,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var p = parameters[i];
|
||||
if (p.IsOverride)
|
||||
if (p.IsOverride && p.IsPublic)
|
||||
{
|
||||
p.IsOverride = false;
|
||||
actions.Add(new EditParamOverrideAction
|
||||
|
||||
@@ -90,25 +90,15 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
|
||||
gpu.CheckBox.Checked = sdfOptions.GPU;
|
||||
gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; };
|
||||
|
||||
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.");
|
||||
var backfacesThreshold = backfacesThresholdProp.FloatValue();
|
||||
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
|
||||
backfacesThreshold.ValueBox.MinValue = 0.001f;
|
||||
backfacesThreshold.ValueBox.MaxValue = 1.0f;
|
||||
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
|
||||
backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; };
|
||||
|
||||
// Toggle Backfaces Threshold visibility (CPU-only option)
|
||||
gpu.CheckBox.StateChanged += c =>
|
||||
{
|
||||
Window._sdfOptions.GPU = c.Checked;
|
||||
backfacesThresholdLabel.Visible = !c.Checked;
|
||||
backfacesThreshold.ValueBox.Visible = !c.Checked;
|
||||
};
|
||||
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
|
||||
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
|
||||
|
||||
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
|
||||
lodIndex.IntValue.MinValue = 0;
|
||||
lodIndex.IntValue.MaxValue = Asset.LODsCount - 1;
|
||||
|
||||
@@ -142,6 +142,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Title = "Content";
|
||||
Icon = editor.Icons.Folder32;
|
||||
var style = Style.Current;
|
||||
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
|
||||
@@ -164,6 +165,8 @@ namespace FlaxEditor.Windows
|
||||
_navigationBar = new NavigationBar
|
||||
{
|
||||
Parent = _toolStrip,
|
||||
ScrollbarTrackColor = style.Background,
|
||||
ScrollbarThumbColor = style.ForegroundGrey,
|
||||
};
|
||||
|
||||
// Split panel
|
||||
@@ -179,7 +182,7 @@ namespace FlaxEditor.Windows
|
||||
var headerPanel = new ContainerControl
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
BackgroundColor = Style.Current.Background,
|
||||
BackgroundColor = style.Background,
|
||||
IsScrollable = false,
|
||||
Offsets = new Margin(0, 0, 0, 18 + 6),
|
||||
};
|
||||
|
||||
@@ -116,6 +116,11 @@ namespace FlaxEditor.Windows
|
||||
if (InputOptions.WindowShortcutsAvaliable)
|
||||
Editor.Windows.VisualScriptDebuggerWin.FocusOrShow();
|
||||
});
|
||||
InputActions.Add(options => options.EditorOptionsWindow, () =>
|
||||
{
|
||||
if (InputOptions.WindowShortcutsAvaliable)
|
||||
Editor.Windows.EditorOptionsWin.FocusOrShow();
|
||||
});
|
||||
|
||||
// Register
|
||||
Editor.Windows.OnWindowAdd(this);
|
||||
|
||||
@@ -10,17 +10,117 @@ using FlaxEditor.Modules;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
namespace FlaxEditor.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Render output control with content scaling support.
|
||||
/// </summary>
|
||||
public class ScaledRenderOutputControl : RenderOutputControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom scale.
|
||||
/// </summary>
|
||||
public float ContentScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Actual bounds size for content (incl. scale).
|
||||
/// </summary>
|
||||
public Float2 ContentSize => Size / ContentScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScaledRenderOutputControl(SceneRenderTask task)
|
||||
: base(task)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
DrawSelf();
|
||||
|
||||
// Draw children with scale
|
||||
var scaling = new Float3(ContentScale, ContentScale, 1);
|
||||
Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
|
||||
Render2D.PushTransform(scale);
|
||||
if (ClipChildren)
|
||||
{
|
||||
GetDesireClientArea(out var clientArea);
|
||||
Render2D.PushClip(ref clientArea);
|
||||
DrawChildren();
|
||||
Render2D.PopClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChildren();
|
||||
}
|
||||
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDesireClientArea(out Rectangle rect)
|
||||
{
|
||||
// Scale the area for the client controls
|
||||
rect = new Rectangle(Float2.Zero, Size / ContentScale);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
|
||||
{
|
||||
// Skip local PointFromParent but use base code
|
||||
location = base.PointFromParent(ref locationParent);
|
||||
return ContainsPoint(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
|
||||
{
|
||||
location /= ContentScale;
|
||||
return base.IntersectsChildContent(child, location, out childSpaceLocation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||
{
|
||||
if (precise) // Ignore as utility-only element
|
||||
return false;
|
||||
return base.ContainsPoint(ref location, precise);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCast(ref Float2 location, out Control hit)
|
||||
{
|
||||
var p = location / ContentScale;
|
||||
if (RayCastChildren(ref p, out hit))
|
||||
return true;
|
||||
return base.RayCast(ref location, out hit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointToParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointToParent(ref location);
|
||||
result *= ContentScale;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointFromParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointFromParent(ref location);
|
||||
result /= ContentScale;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides in-editor play mode simulation.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Windows.EditorWindow" />
|
||||
public class GameWindow : EditorWindow
|
||||
{
|
||||
private readonly RenderOutputControl _viewport;
|
||||
private readonly ScaledRenderOutputControl _viewport;
|
||||
private readonly GameRoot _guiRoot;
|
||||
private bool _showGUI = true, _editGUI = true;
|
||||
private bool _showDebugDraw = false;
|
||||
@@ -77,7 +177,7 @@ namespace FlaxEditor.Windows
|
||||
/// <summary>
|
||||
/// Gets the viewport.
|
||||
/// </summary>
|
||||
public RenderOutputControl Viewport => _viewport;
|
||||
public ScaledRenderOutputControl Viewport => _viewport;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show game GUI in the view or keep it hidden.
|
||||
@@ -295,7 +395,7 @@ namespace FlaxEditor.Windows
|
||||
var task = MainRenderTask.Instance;
|
||||
|
||||
// Setup viewport
|
||||
_viewport = new RenderOutputControl(task)
|
||||
_viewport = new ScaledRenderOutputControl(task)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
@@ -396,11 +496,8 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
if (v.Size.Y <= 0 || v.Size.X <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1))
|
||||
{
|
||||
@@ -448,15 +545,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private void ResizeViewport()
|
||||
{
|
||||
if (!_freeAspect)
|
||||
{
|
||||
_windowAspectRatio = Width / Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
_windowAspectRatio = 1;
|
||||
}
|
||||
|
||||
_windowAspectRatio = _freeAspect ? 1 : Width / Height;
|
||||
var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
|
||||
var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
|
||||
|
||||
@@ -468,6 +557,24 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
|
||||
}
|
||||
|
||||
if (_viewport.KeepAspectRatio)
|
||||
{
|
||||
var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size;
|
||||
if (scaleHeight < 1)
|
||||
{
|
||||
_viewport.ContentScale = _viewport.Width / resolution.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewport.ContentScale = _viewport.Height / resolution.Y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewport.ContentScale = 1;
|
||||
}
|
||||
|
||||
_viewport.SyncBackbufferSize();
|
||||
PerformLayout();
|
||||
}
|
||||
@@ -907,6 +1014,7 @@ namespace FlaxEditor.Windows
|
||||
return child.OnNavigate(direction, Float2.Zero, this, visited);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -957,7 +1065,7 @@ namespace FlaxEditor.Windows
|
||||
else
|
||||
_defaultScaleActiveIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
if (_customScaleActiveIndex != -1)
|
||||
{
|
||||
var options = Editor.UI.CustomViewportScaleOptions;
|
||||
|
||||
@@ -296,13 +296,15 @@ namespace FlaxEditor.Windows.Profiler
|
||||
var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex);
|
||||
if (resources == null || resources.Length == 0)
|
||||
return;
|
||||
var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage);
|
||||
var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0);
|
||||
|
||||
// Add rows
|
||||
var rowColor2 = Style.Current.Background * 1.4f;
|
||||
int rowIndex = 0;
|
||||
foreach (var e in resourcesOrdered)
|
||||
{
|
||||
if (e == null)
|
||||
continue;
|
||||
ClickableRow row;
|
||||
if (_tableRowsCache.Count != 0)
|
||||
{
|
||||
|
||||
@@ -319,22 +319,22 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (assetItem.IsOfType<SceneAsset>())
|
||||
return true;
|
||||
return assetItem.OnEditorDrag(this);
|
||||
return assetItem.OnEditorDrag(this) && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType) && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType) && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -490,6 +490,7 @@ namespace FlaxEditor.Windows
|
||||
if (result == DragDropEffect.None)
|
||||
{
|
||||
_isDropping = true;
|
||||
|
||||
// Drag assets
|
||||
if (_dragAssets != null && _dragAssets.HasValidDrag)
|
||||
{
|
||||
@@ -504,7 +505,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
var graphNode = Editor.Scene.GetActorNode(actor.ID);
|
||||
if (graphNode != null)
|
||||
graphNodes.Add(graphNode);
|
||||
@@ -527,7 +528,7 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
@@ -549,7 +550,7 @@ namespace FlaxEditor.Windows
|
||||
Control = control,
|
||||
Name = item.Name,
|
||||
};
|
||||
Level.SpawnActor(uiControl);
|
||||
Editor.SceneEditing.Spawn(uiControl);
|
||||
Editor.Scene.MarkSceneEdited(uiControl.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
@@ -571,7 +572,7 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
}
|
||||
actor.Name = actorType.Name;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
var graphNode = Editor.Scene.GetActorNode(actor.ID);
|
||||
if (graphNode != null)
|
||||
graphNodes.Add(graphNode);
|
||||
|
||||
@@ -137,6 +137,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Good Luck Have Fun"),
|
||||
TEXT("GG Well Played"),
|
||||
TEXT("Now with documentation."),
|
||||
TEXT("We do this not because it is easy,\nbut because we thought it would be easy"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
@@ -246,11 +246,19 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
|
||||
const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f;
|
||||
#define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type })
|
||||
if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration
|
||||
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
|
||||
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration())
|
||||
&& Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
|
||||
// Handle the edge case of an event on 0 or on max animation duration during looping
|
||||
|| (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration())
|
||||
|| (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|
||||
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|
||||
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f)
|
||||
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f)
|
||||
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|
||||
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|
||||
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f)
|
||||
)
|
||||
{
|
||||
int32 stateIndex = -1;
|
||||
|
||||
@@ -23,11 +23,11 @@ void AudioListener::Update()
|
||||
{
|
||||
// Update the velocity
|
||||
const Vector3 pos = GetPosition();
|
||||
const float dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
||||
const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), 0.00001f);
|
||||
const auto prevVelocity = _velocity;
|
||||
_velocity = (pos - _prevPos) / dt;
|
||||
_prevPos = pos;
|
||||
if (_velocity != prevVelocity)
|
||||
if (_velocity != prevVelocity && !_velocity.IsNanOrInfinity())
|
||||
{
|
||||
AudioBackend::Listener::VelocityChanged(_velocity);
|
||||
}
|
||||
|
||||
@@ -666,7 +666,7 @@ void Asset::onLoaded()
|
||||
{
|
||||
onLoaded_MainThread();
|
||||
}
|
||||
else if (OnLoaded.IsBinded())
|
||||
else if (OnLoaded.IsBinded() || _references.HasItems())
|
||||
{
|
||||
Function<void()> action;
|
||||
action.Bind<Asset, &Asset::onLoaded>(this);
|
||||
|
||||
@@ -218,10 +218,14 @@ Asset::LoadResult MaterialInstance::load()
|
||||
Guid baseMaterialId;
|
||||
headerStream.Read(baseMaterialId);
|
||||
auto baseMaterial = Content::LoadAsync<MaterialBase>(baseMaterialId);
|
||||
if (baseMaterial)
|
||||
baseMaterial->AddReference();
|
||||
|
||||
// Load parameters
|
||||
if (Params.Load(&headerStream))
|
||||
{
|
||||
if (baseMaterial)
|
||||
baseMaterial->RemoveReference();
|
||||
LOG(Warning, "Cannot load material parameters.");
|
||||
return LoadResult::CannotLoadData;
|
||||
}
|
||||
@@ -239,6 +243,8 @@ Asset::LoadResult MaterialInstance::load()
|
||||
ParamsChanged();
|
||||
}
|
||||
|
||||
if (baseMaterial)
|
||||
baseMaterial->RemoveReference();
|
||||
return LoadResult::Ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f
|
||||
LOG(Warning, "Cannot generate SDF for virtual models on a main thread.");
|
||||
return true;
|
||||
}
|
||||
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
|
||||
lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1);
|
||||
|
||||
// Generate SDF
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
model->GetLODData(_lodIndex, data);
|
||||
if (data.IsInvalid())
|
||||
{
|
||||
LOG(Warning, "Missing data chunk");
|
||||
LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString());
|
||||
return true;
|
||||
}
|
||||
MemoryReadStream stream(data.Get(), data.Length());
|
||||
@@ -234,6 +234,7 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
|
||||
return true;
|
||||
}
|
||||
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Use a temporary chunks for data storage for virtual assets
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
/// <param name="id">The asset id.</param>
|
||||
/// <returns>Loaded asset of null.</returns>
|
||||
template<typename T>
|
||||
T* LoadAsync(const Guid& id)
|
||||
T* Load(const Guid& id)
|
||||
{
|
||||
for (auto& e : *this)
|
||||
{
|
||||
@@ -26,8 +26,10 @@ public:
|
||||
return (T*)e.Get();
|
||||
}
|
||||
auto asset = (T*)::LoadAsset(id, T::TypeInitializer);
|
||||
if (asset)
|
||||
if (asset && !asset->WaitForLoaded())
|
||||
Add(asset);
|
||||
else
|
||||
asset = nullptr;
|
||||
return asset;
|
||||
}
|
||||
|
||||
|
||||
@@ -700,6 +700,7 @@ Asset* Content::GetAsset(const StringView& outputPath)
|
||||
{
|
||||
if (outputPath.IsEmpty())
|
||||
return nullptr;
|
||||
PROFILE_CPU();
|
||||
ScopeLock lock(AssetsLocker);
|
||||
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
|
||||
@@ -87,6 +87,10 @@ public:
|
||||
/// </summary>
|
||||
double LastAccessTime = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads).
|
||||
/// </summary>
|
||||
int64 IsLoading = 0;
|
||||
/// <summary>
|
||||
/// The chunk data.
|
||||
/// </summary>
|
||||
@@ -146,7 +150,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsLoaded() const
|
||||
{
|
||||
return Data.IsValid();
|
||||
return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,7 +158,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsMissing() const
|
||||
{
|
||||
return Data.IsInvalid();
|
||||
return !IsLoaded();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "FlaxPackage.h"
|
||||
#include "ContentStorageManager.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/ScopeExit.h"
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
@@ -74,8 +75,6 @@ FlaxChunk* FlaxChunk::Clone() const
|
||||
|
||||
const int32 FlaxStorage::MagicCode = 1180124739;
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
|
||||
|
||||
struct Header
|
||||
{
|
||||
int32 MagicCode;
|
||||
@@ -246,6 +245,7 @@ FlaxStorage::~FlaxStorage()
|
||||
ASSERT(IsDisposed());
|
||||
CHECK(_chunksLock == 0);
|
||||
CHECK(_refCount == 0);
|
||||
CHECK(_isUnloadingData == 0);
|
||||
ASSERT(_chunks.IsEmpty());
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -261,6 +261,22 @@ FlaxStorage::~FlaxStorage()
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlaxStorage::LockChunks()
|
||||
{
|
||||
RETRY:
|
||||
Platform::InterlockedIncrement(&_chunksLock);
|
||||
if (Platform::AtomicRead(&_isUnloadingData) != 0)
|
||||
{
|
||||
// Someone else is closing file handles or freeing chunks so wait for it to finish and retry
|
||||
Platform::InterlockedDecrement(&_chunksLock);
|
||||
do
|
||||
{
|
||||
Platform::Sleep(1);
|
||||
} while (Platform::AtomicRead(&_isUnloadingData) != 0);
|
||||
goto RETRY;
|
||||
}
|
||||
}
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockSafe()
|
||||
{
|
||||
auto lock = LockData(this);
|
||||
@@ -689,7 +705,6 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load header
|
||||
return LoadAssetHeader(e, data);
|
||||
}
|
||||
|
||||
@@ -699,7 +714,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
ASSERT(IsLoaded());
|
||||
ASSERT(chunk != nullptr && _chunks.Contains(chunk));
|
||||
|
||||
// Check if already loaded
|
||||
// Protect against loading the same chunk from multiple threads at once
|
||||
while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0)
|
||||
Platform::Sleep(1);
|
||||
SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); };
|
||||
if (chunk->IsLoaded())
|
||||
return false;
|
||||
|
||||
@@ -776,12 +794,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
// Raw data
|
||||
chunk->Data.Read(stream, size);
|
||||
}
|
||||
ASSERT(chunk->IsLoaded());
|
||||
chunk->RegisterUsage();
|
||||
}
|
||||
|
||||
UnlockChunks();
|
||||
|
||||
return failed;
|
||||
}
|
||||
|
||||
@@ -1420,10 +1436,12 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
|
||||
bool FlaxStorage::CloseFileHandles()
|
||||
{
|
||||
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
|
||||
|
||||
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false; // Early out when no files are opened
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(ContentFiles);
|
||||
|
||||
@@ -1496,9 +1514,21 @@ void FlaxStorage::Tick(double time)
|
||||
{
|
||||
auto chunk = _chunks.Get()[i];
|
||||
const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime;
|
||||
if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory))
|
||||
if (!wasUsed &&
|
||||
chunk->IsLoaded() &&
|
||||
EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) &&
|
||||
Platform::AtomicRead(&chunk->IsLoading) == 0)
|
||||
{
|
||||
// Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0)
|
||||
{
|
||||
// Someone started loading so skip ticking
|
||||
Platform::InterlockedDecrement(&_isUnloadingData);
|
||||
return;
|
||||
}
|
||||
chunk->Unload();
|
||||
Platform::InterlockedDecrement(&_isUnloadingData);
|
||||
}
|
||||
wasAnyUsed |= wasUsed;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ protected:
|
||||
int64 _refCount = 0;
|
||||
int64 _chunksLock = 0;
|
||||
int64 _files = 0;
|
||||
int64 _isUnloadingData = 0;
|
||||
double _lastRefLostTime;
|
||||
CriticalSection _loadLocker;
|
||||
|
||||
@@ -129,10 +130,7 @@ public:
|
||||
/// <summary>
|
||||
/// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked.
|
||||
/// </summary>
|
||||
FORCE_INLINE void LockChunks()
|
||||
{
|
||||
Platform::InterlockedIncrement(&_chunksLock);
|
||||
}
|
||||
void LockChunks();
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks the storage chunks data.
|
||||
@@ -148,7 +146,6 @@ public:
|
||||
struct LockData
|
||||
{
|
||||
friend FlaxStorage;
|
||||
static LockData Invalid;
|
||||
|
||||
private:
|
||||
FlaxStorage* _storage;
|
||||
@@ -161,6 +158,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
LockData()
|
||||
: _storage(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
LockData(const LockData& other)
|
||||
: _storage(other._storage)
|
||||
{
|
||||
|
||||
@@ -620,14 +620,9 @@ bool Collision::RayIntersectsTriangle(const Ray& ray, const Vector3& a, const Ve
|
||||
Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z;
|
||||
rayDistance *= inverseDeterminant;
|
||||
|
||||
// Check if the triangle is behind the ray origin
|
||||
if (rayDistance < 0.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the triangle is in front the ray origin
|
||||
distance = rayDistance;
|
||||
return true;
|
||||
return rayDistance >= 0.0f;
|
||||
}
|
||||
|
||||
bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal)
|
||||
|
||||
@@ -12,11 +12,6 @@ String Ray::ToString() const
|
||||
return String::Format(TEXT("{}"), *this);
|
||||
}
|
||||
|
||||
Vector3 Ray::GetPoint(Real distance) const
|
||||
{
|
||||
return Position + Direction * distance;
|
||||
}
|
||||
|
||||
Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp)
|
||||
{
|
||||
Vector3 nearPoint(x, y, 0.0f);
|
||||
|
||||
@@ -79,7 +79,10 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="distance">The distance from ray origin.</param>
|
||||
/// <returns>The calculated point.</returns>
|
||||
Vector3 GetPoint(Real distance) const;
|
||||
FORCE_INLINE Vector3 GetPoint(Real distance) const
|
||||
{
|
||||
return Position + Direction * distance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if there is an intersection between ray and a point.
|
||||
|
||||
@@ -208,7 +208,7 @@ public:
|
||||
typedef typename FallbackAllocation::template Data<T> FallbackData;
|
||||
|
||||
bool _useFallback = false;
|
||||
byte _data[Capacity * sizeof(T)];
|
||||
alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)];
|
||||
FallbackData _fallback;
|
||||
|
||||
public:
|
||||
|
||||
@@ -215,6 +215,11 @@ public:
|
||||
return String(_data.Get(), _data.Count());
|
||||
}
|
||||
|
||||
StringAnsi ToStringAnsi() const
|
||||
{
|
||||
return StringAnsi(_data.Get(), _data.Count());
|
||||
}
|
||||
|
||||
StringView ToStringView() const;
|
||||
};
|
||||
|
||||
|
||||
@@ -425,6 +425,8 @@ namespace FlaxEngine.Interop
|
||||
var fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField
|
||||
fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags)
|
||||
return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync)
|
||||
#else
|
||||
throw new NotImplementedException();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -338,10 +338,10 @@ public:
|
||||
StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex, Task* rootTask)
|
||||
: GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span<byte>(nullptr, 0), 0, 0, false)
|
||||
, _streamingTexture(texture)
|
||||
, _rootTask(rootTask ? rootTask : this)
|
||||
, _rootTask(rootTask)
|
||||
, _dataLock(_streamingTexture->GetOwner()->LockData())
|
||||
{
|
||||
_streamingTexture->_streamingTasks.Add(_rootTask);
|
||||
_streamingTexture->_streamingTasks.Add(this);
|
||||
_texture.Released.Bind<StreamTextureMipTask, &StreamTextureMipTask::OnResourceReleased2>(this);
|
||||
}
|
||||
|
||||
@@ -357,7 +357,7 @@ private:
|
||||
if (_streamingTexture)
|
||||
{
|
||||
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
|
||||
_streamingTexture->_streamingTasks.Remove(_rootTask);
|
||||
_streamingTexture->_streamingTasks.Remove(this);
|
||||
_streamingTexture = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -422,6 +422,15 @@ protected:
|
||||
|
||||
GPUUploadTextureMipTask::OnFail();
|
||||
}
|
||||
|
||||
void OnCancel() override
|
||||
{
|
||||
GPUUploadTextureMipTask::OnCancel();
|
||||
|
||||
// Cancel the root task too (eg. mip loading from asset)
|
||||
if (_rootTask != nullptr)
|
||||
_rootTask->Cancel();
|
||||
}
|
||||
};
|
||||
|
||||
Task* StreamingTexture::CreateStreamingTask(int32 residency)
|
||||
|
||||
@@ -771,7 +771,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex)
|
||||
|
||||
FlaxStorage::LockData TextureBase::LockData()
|
||||
{
|
||||
return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData::Invalid;
|
||||
return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData();
|
||||
}
|
||||
|
||||
void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const
|
||||
|
||||
@@ -92,9 +92,8 @@ float GPUTimerQueryDX11::GetResult()
|
||||
{
|
||||
if (!_finalized)
|
||||
{
|
||||
#if BUILD_DEBUG
|
||||
ASSERT(HasResult());
|
||||
#endif
|
||||
if (!HasResult())
|
||||
return 0;
|
||||
|
||||
UINT64 timeStart, timeEnd;
|
||||
auto context = _device->GetIM();
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "GPUVertexLayoutDX12.h"
|
||||
#include "CommandQueueDX12.h"
|
||||
#include "DescriptorHeapDX12.h"
|
||||
#include "RootSignatureDX12.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h"
|
||||
#include "Engine/Debug/Exceptions/NotImplementedException.h"
|
||||
@@ -307,6 +308,7 @@ void GPUContextDX12::Reset()
|
||||
_device->DummyVB = _device->CreateBuffer(TEXT("DummyVertexBuffer"));
|
||||
auto* layout = GPUVertexLayout::Get({ { VertexElement::Types::Attribute3, 0, 0, 0, PixelFormat::R32G32B32A32_Float } });
|
||||
_device->DummyVB->Init(GPUBufferDescription::Vertex(layout, sizeof(Color), 1, &Color::Transparent));
|
||||
SetResourceState(_device->DummyVB, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, 0);
|
||||
}
|
||||
((GPUBufferDX12*)_device->DummyVB)->GetVBView(dummyVBView);
|
||||
_commandList->IASetVertexBuffers(GPU_MAX_VB_BINDED, 1, &dummyVBView);
|
||||
@@ -491,7 +493,7 @@ void GPUContextDX12::flushUAVs()
|
||||
ASSERT(uaCount <= GPU_MAX_UA_BINDED);
|
||||
|
||||
// Fill table with source descriptors
|
||||
DxShaderHeader& header = _currentCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header;
|
||||
DxShaderHeader& header = _isCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_UA_BINDED];
|
||||
for (uint32 i = 0; i < uaCount; i++)
|
||||
{
|
||||
@@ -628,7 +630,9 @@ void GPUContextDX12::flushPS()
|
||||
LOG(Error, "Missing Vertex Layout (not assigned to GPUBuffer). Vertex Shader won't read valid data resulting incorrect visuals.");
|
||||
}
|
||||
#endif
|
||||
_commandList->SetPipelineState(_currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout));
|
||||
ID3D12PipelineState* pso = _currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout);
|
||||
ASSERT(pso);
|
||||
_commandList->SetPipelineState(pso);
|
||||
if (_primitiveTopology != _currentState->PrimitiveTopology)
|
||||
{
|
||||
_primitiveTopology = _currentState->PrimitiveTopology;
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include "GPUSamplerDX12.h"
|
||||
#include "GPUVertexLayoutDX12.h"
|
||||
#include "GPUSwapChainDX12.h"
|
||||
#include "RootSignatureDX12.h"
|
||||
#include "UploadBufferDX12.h"
|
||||
#include "CommandQueueDX12.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
@@ -21,20 +24,23 @@
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Config/PlatformSettings.h"
|
||||
#include "UploadBufferDX12.h"
|
||||
#include "CommandQueueDX12.h"
|
||||
#include "Engine/Core/Types/StringBuilder.h"
|
||||
#include "Engine/Core/Utilities.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "CommandSignatureDX12.h"
|
||||
|
||||
static bool CheckDX12Support(IDXGIAdapter* adapter)
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
|
||||
return true;
|
||||
#else
|
||||
// Try to create device
|
||||
if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets)
|
||||
@@ -55,6 +61,310 @@ GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements&
|
||||
}
|
||||
}
|
||||
|
||||
RootSignatureDX12::RootSignatureDX12()
|
||||
{
|
||||
// Clear structures
|
||||
Platform::MemoryClear(this, sizeof(*this));
|
||||
|
||||
// Descriptor tables
|
||||
{
|
||||
// SRVs
|
||||
D3D12_DESCRIPTOR_RANGE& range = _ranges[0];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
range.NumDescriptors = GPU_MAX_SR_BINDED;
|
||||
range.BaseShaderRegister = 0;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
{
|
||||
// UAVs
|
||||
D3D12_DESCRIPTOR_RANGE& range = _ranges[1];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
range.NumDescriptors = GPU_MAX_UA_BINDED;
|
||||
range.BaseShaderRegister = 0;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
{
|
||||
// Samplers
|
||||
D3D12_DESCRIPTOR_RANGE& range = _ranges[2];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
||||
range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT;
|
||||
range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
|
||||
// Root parameters
|
||||
for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++)
|
||||
{
|
||||
// CBs
|
||||
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_CB + i];
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
param.Descriptor.ShaderRegister = i;
|
||||
param.Descriptor.RegisterSpace = 0;
|
||||
}
|
||||
{
|
||||
// SRVs
|
||||
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SR];
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
param.DescriptorTable.NumDescriptorRanges = 1;
|
||||
param.DescriptorTable.pDescriptorRanges = &_ranges[0];
|
||||
}
|
||||
{
|
||||
// UAVs
|
||||
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_UA];
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
param.DescriptorTable.NumDescriptorRanges = 1;
|
||||
param.DescriptorTable.pDescriptorRanges = &_ranges[1];
|
||||
}
|
||||
{
|
||||
// Samplers
|
||||
D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SAMPLER];
|
||||
param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
param.DescriptorTable.NumDescriptorRanges = 1;
|
||||
param.DescriptorTable.pDescriptorRanges = &_ranges[2];
|
||||
}
|
||||
|
||||
// Static samplers
|
||||
static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(_staticSamplers), "Update static samplers setup.");
|
||||
// Linear Clamp
|
||||
InitSampler(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
|
||||
// Point Clamp
|
||||
InitSampler(1, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP);
|
||||
// Linear Wrap
|
||||
InitSampler(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP);
|
||||
// Point Wrap
|
||||
InitSampler(3, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_WRAP);
|
||||
// Shadow
|
||||
InitSampler(4, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL);
|
||||
// Shadow PCF
|
||||
InitSampler(5, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL);
|
||||
|
||||
// Init
|
||||
_desc.NumParameters = ARRAY_COUNT(_parameters);
|
||||
_desc.pParameters = _parameters;
|
||||
_desc.NumStaticSamplers = ARRAY_COUNT(_staticSamplers);
|
||||
_desc.pStaticSamplers = _staticSamplers;
|
||||
_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
}
|
||||
|
||||
void RootSignatureDX12::InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc)
|
||||
{
|
||||
auto& sampler = _staticSamplers[i];
|
||||
sampler.Filter = filter;
|
||||
sampler.AddressU = address;
|
||||
sampler.AddressV = address;
|
||||
sampler.AddressW = address;
|
||||
sampler.MipLODBias = 0.0f;
|
||||
sampler.MaxAnisotropy = 1;
|
||||
sampler.ComparisonFunc = comparisonFunc;
|
||||
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
sampler.MinLOD = 0;
|
||||
sampler.MaxLOD = D3D12_FLOAT32_MAX;
|
||||
sampler.ShaderRegister = i;
|
||||
sampler.RegisterSpace = 0;
|
||||
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
}
|
||||
|
||||
ComPtr<ID3DBlob> RootSignatureDX12::Serialize() const
|
||||
{
|
||||
ComPtr<ID3DBlob> signature;
|
||||
ComPtr<ID3DBlob> error;
|
||||
VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&_desc, D3D_ROOT_SIGNATURE_VERSION_1_0, &signature, &error));
|
||||
if (error.Get())
|
||||
{
|
||||
LOG(Error, "D3D12SerializeRootSignature failed with error: {}", String((const char*)error->GetBufferPointer()));
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
const Char* GetRootSignatureShaderVisibility(D3D12_SHADER_VISIBILITY visibility)
|
||||
{
|
||||
switch (visibility)
|
||||
{
|
||||
case D3D12_SHADER_VISIBILITY_VERTEX:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_VERTEX");
|
||||
case D3D12_SHADER_VISIBILITY_HULL:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_HULL");
|
||||
case D3D12_SHADER_VISIBILITY_DOMAIN:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_DOMAIN");
|
||||
case D3D12_SHADER_VISIBILITY_GEOMETRY:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_GEOMETRY");
|
||||
case D3D12_SHADER_VISIBILITY_PIXEL:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_PIXEL");
|
||||
case D3D12_SHADER_VISIBILITY_AMPLIFICATION:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_AMPLIFICATION");
|
||||
case D3D12_SHADER_VISIBILITY_MESH:
|
||||
return TEXT(", visibility=SHADER_VISIBILITY_MESH");
|
||||
case D3D12_SHADER_VISIBILITY_ALL:
|
||||
default:
|
||||
return TEXT(""); // Default
|
||||
}
|
||||
}
|
||||
|
||||
const Char* GetRootSignatureSamplerFilter(D3D12_FILTER filter)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case D3D12_FILTER_MIN_MAG_MIP_POINT:
|
||||
return TEXT("FILTER_MIN_MAG_MIP_POINT");
|
||||
case D3D12_FILTER_MIN_MAG_MIP_LINEAR:
|
||||
return TEXT("FILTER_MIN_MAG_MIP_LINEAR");
|
||||
case D3D12_FILTER_ANISOTROPIC:
|
||||
return TEXT("FILTER_ANISOTROPIC");
|
||||
case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT:
|
||||
return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_POINT");
|
||||
case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR:
|
||||
return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_LINEAR");
|
||||
default:
|
||||
CRASH; // Not implemented
|
||||
}
|
||||
}
|
||||
|
||||
const Char* GetRootSignatureSamplerAddress(D3D12_TEXTURE_ADDRESS_MODE address)
|
||||
{
|
||||
switch (address)
|
||||
{
|
||||
case D3D12_TEXTURE_ADDRESS_MODE_WRAP:
|
||||
return TEXT("TEXTURE_ADDRESS_WRAP");
|
||||
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR:
|
||||
return TEXT("TEXTURE_ADDRESS_MIRROR");
|
||||
case D3D12_TEXTURE_ADDRESS_MODE_CLAMP:
|
||||
return TEXT("TEXTURE_ADDRESS_CLAMP");
|
||||
case D3D12_TEXTURE_ADDRESS_MODE_BORDER:
|
||||
return TEXT("TEXTURE_ADDRESS_BORDER");
|
||||
case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE:
|
||||
return TEXT("TEXTURE_ADDRESS_MIRROR_ONCE");
|
||||
default:
|
||||
return TEXT("");
|
||||
}
|
||||
}
|
||||
|
||||
const Char* GetRootSignatureSamplerComparisonFunc(D3D12_COMPARISON_FUNC func)
|
||||
{
|
||||
switch (func)
|
||||
{
|
||||
case D3D12_COMPARISON_FUNC_NEVER:
|
||||
return TEXT("COMPARISON_NEVER");
|
||||
case D3D12_COMPARISON_FUNC_LESS:
|
||||
return TEXT("COMPARISON_LESS");
|
||||
case D3D12_COMPARISON_FUNC_EQUAL:
|
||||
return TEXT("COMPARISON_EQUAL");
|
||||
case D3D12_COMPARISON_FUNC_LESS_EQUAL:
|
||||
return TEXT("COMPARISON_LESS_EQUAL");
|
||||
case D3D12_COMPARISON_FUNC_GREATER:
|
||||
return TEXT("COMPARISON_GREATER");
|
||||
case D3D12_COMPARISON_FUNC_NOT_EQUAL:
|
||||
return TEXT("COMPARISON_NOT_EQUAL");
|
||||
case D3D12_COMPARISON_FUNC_GREATER_EQUAL:
|
||||
return TEXT("COMPARISON_GREATER_EQUAL");
|
||||
case D3D12_COMPARISON_FUNC_ALWAYS:
|
||||
default:
|
||||
return TEXT("COMPARISON_ALWAYS");
|
||||
}
|
||||
}
|
||||
|
||||
void RootSignatureDX12::ToString(StringBuilder& sb, bool singleLine) const
|
||||
{
|
||||
// Flags
|
||||
sb.Append(TEXT("RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)"));
|
||||
|
||||
// Parameters
|
||||
const Char newLine = singleLine ? ' ' : '\n';
|
||||
for (const D3D12_ROOT_PARAMETER& param : _parameters)
|
||||
{
|
||||
const Char* visibility = GetRootSignatureShaderVisibility(param.ShaderVisibility);
|
||||
switch (param.ParameterType)
|
||||
{
|
||||
case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE:
|
||||
sb.AppendFormat(TEXT(",{}DescriptorTable("), newLine);
|
||||
for (uint32 rangeIndex = 0; rangeIndex < param.DescriptorTable.NumDescriptorRanges; rangeIndex++)
|
||||
{
|
||||
if (rangeIndex)
|
||||
sb.Append(TEXT(", "));
|
||||
const D3D12_DESCRIPTOR_RANGE& range = param.DescriptorTable.pDescriptorRanges[rangeIndex];
|
||||
switch (range.RangeType)
|
||||
{
|
||||
case D3D12_DESCRIPTOR_RANGE_TYPE_SRV:
|
||||
sb.AppendFormat(TEXT("SRV(t{}"), range.BaseShaderRegister);
|
||||
break;
|
||||
case D3D12_DESCRIPTOR_RANGE_TYPE_UAV:
|
||||
sb.AppendFormat(TEXT("UAV(u{}"), range.BaseShaderRegister);
|
||||
break;
|
||||
case D3D12_DESCRIPTOR_RANGE_TYPE_CBV:
|
||||
sb.AppendFormat(TEXT("CBV(b{}"), range.BaseShaderRegister);
|
||||
break;
|
||||
case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER:
|
||||
sb.AppendFormat(TEXT("Sampler(s{}"), range.BaseShaderRegister);
|
||||
break;
|
||||
}
|
||||
if (range.NumDescriptors != 1)
|
||||
{
|
||||
if (range.NumDescriptors == UINT_MAX)
|
||||
sb.Append(TEXT(", numDescriptors=unbounded"));
|
||||
else
|
||||
sb.AppendFormat(TEXT(", numDescriptors={}"), range.NumDescriptors);
|
||||
}
|
||||
if (range.OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND)
|
||||
sb.AppendFormat(TEXT(", offset={}"), range.OffsetInDescriptorsFromTableStart);
|
||||
sb.Append(')');
|
||||
}
|
||||
sb.AppendFormat(TEXT("{})"), visibility);
|
||||
break;
|
||||
case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS:
|
||||
sb.AppendFormat(TEXT(",{}RootConstants(num32BitConstants={}, b{}{})"), newLine, param.Constants.Num32BitValues, param.Constants.ShaderRegister, visibility);
|
||||
break;
|
||||
case D3D12_ROOT_PARAMETER_TYPE_CBV:
|
||||
sb.AppendFormat(TEXT(",{}CBV(b{}{})"), newLine, param.Descriptor.ShaderRegister, visibility);
|
||||
break;
|
||||
case D3D12_ROOT_PARAMETER_TYPE_SRV:
|
||||
sb.AppendFormat(TEXT(",{}SRV(t{}{})"), newLine, param.Descriptor.ShaderRegister, visibility);
|
||||
break;
|
||||
case D3D12_ROOT_PARAMETER_TYPE_UAV:
|
||||
sb.AppendFormat(TEXT(",{}UAV(u{}{})"), newLine, param.Descriptor.ShaderRegister, visibility);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Static Samplers
|
||||
for (const D3D12_STATIC_SAMPLER_DESC& sampler : _staticSamplers)
|
||||
{
|
||||
const Char* visibility = GetRootSignatureShaderVisibility(sampler.ShaderVisibility);
|
||||
sb.AppendFormat(TEXT(",{}StaticSampler(s{}"), newLine, sampler.ShaderRegister);
|
||||
sb.AppendFormat(TEXT(", filter={}"), GetRootSignatureSamplerFilter(sampler.Filter));
|
||||
sb.AppendFormat(TEXT(", addressU={}"), GetRootSignatureSamplerAddress(sampler.AddressU));
|
||||
sb.AppendFormat(TEXT(", addressV={}"), GetRootSignatureSamplerAddress(sampler.AddressV));
|
||||
sb.AppendFormat(TEXT(", addressW={}"), GetRootSignatureSamplerAddress(sampler.AddressW));
|
||||
sb.AppendFormat(TEXT(", comparisonFunc={}"), GetRootSignatureSamplerComparisonFunc(sampler.ComparisonFunc));
|
||||
sb.AppendFormat(TEXT(", maxAnisotropy={}"), sampler.MaxAnisotropy);
|
||||
sb.Append(TEXT(", borderColor=STATIC_BORDER_COLOR_OPAQUE_BLACK"));
|
||||
sb.AppendFormat(TEXT("{})"), visibility);
|
||||
}
|
||||
}
|
||||
|
||||
String RootSignatureDX12::ToString() const
|
||||
{
|
||||
StringBuilder sb;
|
||||
ToString(sb);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
StringAnsi RootSignatureDX12::ToStringAnsi() const
|
||||
{
|
||||
StringBuilder sb;
|
||||
ToString(sb);
|
||||
return sb.ToStringAnsi();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GPUDevice* GPUDeviceDX12::Create()
|
||||
{
|
||||
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
|
||||
@@ -561,170 +871,10 @@ bool GPUDeviceDX12::Init()
|
||||
}
|
||||
|
||||
// Create root signature
|
||||
// TODO: maybe create set of different root signatures? for UAVs, for compute, for simple drawing, for post fx?
|
||||
{
|
||||
// Descriptor tables
|
||||
D3D12_DESCRIPTOR_RANGE r[3]; // SRV+UAV+Sampler
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE& range = r[0];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
|
||||
range.NumDescriptors = GPU_MAX_SR_BINDED;
|
||||
range.BaseShaderRegister = 0;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE& range = r[1];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
|
||||
range.NumDescriptors = GPU_MAX_UA_BINDED;
|
||||
range.BaseShaderRegister = 0;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE& range = r[2];
|
||||
range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER;
|
||||
range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT;
|
||||
range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT;
|
||||
range.RegisterSpace = 0;
|
||||
range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
|
||||
}
|
||||
|
||||
// Root parameters
|
||||
D3D12_ROOT_PARAMETER rootParameters[GPU_MAX_CB_BINDED + 3];
|
||||
for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++)
|
||||
{
|
||||
// CB
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_CB + i];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.Descriptor.ShaderRegister = i;
|
||||
rootParam.Descriptor.RegisterSpace = 0;
|
||||
}
|
||||
{
|
||||
// SRVs
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SR];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.DescriptorTable.NumDescriptorRanges = 1;
|
||||
rootParam.DescriptorTable.pDescriptorRanges = &r[0];
|
||||
}
|
||||
{
|
||||
// UAVs
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_UA];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.DescriptorTable.NumDescriptorRanges = 1;
|
||||
rootParam.DescriptorTable.pDescriptorRanges = &r[1];
|
||||
}
|
||||
{
|
||||
// Samplers
|
||||
D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SAMPLER];
|
||||
rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
|
||||
rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
rootParam.DescriptorTable.NumDescriptorRanges = 1;
|
||||
rootParam.DescriptorTable.pDescriptorRanges = &r[2];
|
||||
}
|
||||
|
||||
// Static samplers
|
||||
D3D12_STATIC_SAMPLER_DESC staticSamplers[6];
|
||||
static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(staticSamplers), "Update static samplers setup.");
|
||||
// Linear Clamp
|
||||
staticSamplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[0].MipLODBias = 0.0f;
|
||||
staticSamplers[0].MaxAnisotropy = 1;
|
||||
staticSamplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[0].MinLOD = 0;
|
||||
staticSamplers[0].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[0].ShaderRegister = 0;
|
||||
staticSamplers[0].RegisterSpace = 0;
|
||||
staticSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Point Clamp
|
||||
staticSamplers[1].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
||||
staticSamplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[1].MipLODBias = 0.0f;
|
||||
staticSamplers[1].MaxAnisotropy = 1;
|
||||
staticSamplers[1].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[1].MinLOD = 0;
|
||||
staticSamplers[1].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[1].ShaderRegister = 1;
|
||||
staticSamplers[1].RegisterSpace = 0;
|
||||
staticSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Linear Wrap
|
||||
staticSamplers[2].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[2].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[2].MipLODBias = 0.0f;
|
||||
staticSamplers[2].MaxAnisotropy = 1;
|
||||
staticSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[2].MinLOD = 0;
|
||||
staticSamplers[2].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[2].ShaderRegister = 2;
|
||||
staticSamplers[2].RegisterSpace = 0;
|
||||
staticSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Point Wrap
|
||||
staticSamplers[3].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
||||
staticSamplers[3].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[3].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[3].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSamplers[3].MipLODBias = 0.0f;
|
||||
staticSamplers[3].MaxAnisotropy = 1;
|
||||
staticSamplers[3].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[3].MinLOD = 0;
|
||||
staticSamplers[3].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[3].ShaderRegister = 3;
|
||||
staticSamplers[3].RegisterSpace = 0;
|
||||
staticSamplers[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Shadow
|
||||
staticSamplers[4].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT;
|
||||
staticSamplers[4].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[4].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[4].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[4].MipLODBias = 0.0f;
|
||||
staticSamplers[4].MaxAnisotropy = 1;
|
||||
staticSamplers[4].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
|
||||
staticSamplers[4].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[4].MinLOD = 0;
|
||||
staticSamplers[4].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[4].ShaderRegister = 4;
|
||||
staticSamplers[4].RegisterSpace = 0;
|
||||
staticSamplers[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
// Shadow PCF
|
||||
staticSamplers[5].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
|
||||
staticSamplers[5].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[5].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[5].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSamplers[5].MipLODBias = 0.0f;
|
||||
staticSamplers[5].MaxAnisotropy = 1;
|
||||
staticSamplers[5].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL;
|
||||
staticSamplers[5].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK;
|
||||
staticSamplers[5].MinLOD = 0;
|
||||
staticSamplers[5].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSamplers[5].ShaderRegister = 5;
|
||||
staticSamplers[5].RegisterSpace = 0;
|
||||
staticSamplers[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
|
||||
|
||||
// Init
|
||||
D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc;
|
||||
rootSignatureDesc.NumParameters = ARRAY_COUNT(rootParameters);
|
||||
rootSignatureDesc.pParameters = rootParameters;
|
||||
rootSignatureDesc.NumStaticSamplers = ARRAY_COUNT(staticSamplers);
|
||||
rootSignatureDesc.pStaticSamplers = staticSamplers;
|
||||
rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
|
||||
|
||||
// Serialize
|
||||
ComPtr<ID3DBlob> signature;
|
||||
ComPtr<ID3DBlob> error;
|
||||
VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
|
||||
|
||||
// Create
|
||||
VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature)));
|
||||
RootSignatureDX12 signature;
|
||||
ComPtr<ID3DBlob> signatureBlob = signature.Serialize();
|
||||
VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&_rootSignature)));
|
||||
}
|
||||
|
||||
if (TimestampQueryHeap.Init())
|
||||
|
||||
@@ -18,11 +18,6 @@
|
||||
#define DX12_BACK_BUFFER_COUNT 2
|
||||
#endif
|
||||
|
||||
#define DX12_ROOT_SIGNATURE_CB 0
|
||||
#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0)
|
||||
#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1)
|
||||
#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2)
|
||||
|
||||
class Engine;
|
||||
class WindowsWindow;
|
||||
class GPUContextDX12;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Graphics/Config.h"
|
||||
#include "../IncludeDirectXHeaders.h"
|
||||
|
||||
#define DX12_ROOT_SIGNATURE_CB 0
|
||||
#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0)
|
||||
#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1)
|
||||
#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2)
|
||||
|
||||
struct RootSignatureDX12
|
||||
{
|
||||
private:
|
||||
D3D12_ROOT_SIGNATURE_DESC _desc;
|
||||
D3D12_DESCRIPTOR_RANGE _ranges[3];
|
||||
D3D12_ROOT_PARAMETER _parameters[GPU_MAX_CB_BINDED + 3];
|
||||
D3D12_STATIC_SAMPLER_DESC _staticSamplers[6];
|
||||
|
||||
public:
|
||||
RootSignatureDX12();
|
||||
|
||||
ComPtr<ID3DBlob> Serialize() const;
|
||||
#if USE_EDITOR
|
||||
void ToString(class StringBuilder& sb, bool singleLine = false) const;
|
||||
String ToString() const;
|
||||
StringAnsi ToStringAnsi() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL);
|
||||
};
|
||||
@@ -114,7 +114,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
|
||||
#if GPU_ENABLE_TRACY
|
||||
#if VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset && !PLATFORM_SWITCH
|
||||
// Use calibrated timestamps extension
|
||||
if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT)
|
||||
if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT && _device->PhysicalDeviceFeatures12.hostQueryReset)
|
||||
{
|
||||
_tracyContext = tracy::CreateVkContext(_device->Adapter->Gpu, _device->Device, vkResetQueryPoolEXT, vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, vkGetCalibratedTimestampsEXT);
|
||||
}
|
||||
|
||||
@@ -1568,7 +1568,15 @@ bool GPUDeviceVulkan::Init()
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queueCount, QueueFamilyProps.Get());
|
||||
|
||||
// Query device features
|
||||
RenderToolsVulkan::ZeroStruct(PhysicalDeviceFeatures12, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES);
|
||||
vkGetPhysicalDeviceFeatures(gpu, &PhysicalDeviceFeatures);
|
||||
if (vkGetPhysicalDeviceFeatures2)
|
||||
{
|
||||
VkPhysicalDeviceFeatures2 features2;
|
||||
RenderToolsVulkan::ZeroStruct(features2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2);
|
||||
features2.pNext = &PhysicalDeviceFeatures12;
|
||||
vkGetPhysicalDeviceFeatures2(gpu, &features2);
|
||||
}
|
||||
|
||||
// Get extensions and layers
|
||||
Array<const char*> deviceExtensions;
|
||||
@@ -1671,6 +1679,16 @@ bool GPUDeviceVulkan::Init()
|
||||
VulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(PhysicalDeviceFeatures, enabledFeatures);
|
||||
deviceInfo.pEnabledFeatures = &enabledFeatures;
|
||||
|
||||
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
|
||||
VkPhysicalDeviceHostQueryResetFeatures resetFeatures;
|
||||
if (PhysicalDeviceFeatures12.hostQueryReset)
|
||||
{
|
||||
RenderToolsVulkan::ZeroStruct(resetFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES);
|
||||
resetFeatures.hostQueryReset = VK_TRUE;
|
||||
deviceInfo.pNext = &resetFeatures;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create the device
|
||||
VALIDATE_VULKAN_RESULT(vkCreateDevice(gpu, &deviceInfo, nullptr, &Device));
|
||||
|
||||
|
||||
@@ -496,6 +496,7 @@ public:
|
||||
/// The physical device enabled features.
|
||||
/// </summary>
|
||||
VkPhysicalDeviceFeatures PhysicalDeviceFeatures;
|
||||
VkPhysicalDeviceVulkan12Features PhysicalDeviceFeatures12;
|
||||
|
||||
Array<BufferedQueryPoolVulkan*> TimestampQueryPools;
|
||||
Array<BufferedQueryPoolVulkan*> OcclusionQueryPools;
|
||||
|
||||
@@ -42,38 +42,38 @@ public:
|
||||
/// <summary>
|
||||
/// The reflections texture resolution.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Probe\")")
|
||||
API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Quality\")")
|
||||
ProbeCubemapResolution CubemapResolution = ProbeCubemapResolution::UseGraphicsSettings;
|
||||
|
||||
/// <summary>
|
||||
/// The probe update mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorOrder(10), EditorDisplay(\"Quality\")")
|
||||
ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual;
|
||||
|
||||
/// <summary>
|
||||
/// The reflections brightness.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")")
|
||||
API_FIELD(Attributes="EditorOrder(0), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")")
|
||||
float Brightness = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The probe rendering order. The higher values are render later (on top).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorOrder(25), EditorDisplay(\"Probe\")")
|
||||
API_FIELD(Attributes = "EditorOrder(20), EditorDisplay(\"Probe\")")
|
||||
int32 SortOrder = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The probe update mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Probe\")")
|
||||
ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual;
|
||||
|
||||
/// <summary>
|
||||
/// The probe capture camera near plane distance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(30), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")")
|
||||
API_FIELD(Attributes="EditorOrder(25), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")")
|
||||
float CaptureNearPlane = 10.0f;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the probe radius.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")")
|
||||
API_PROPERTY(Attributes="EditorOrder(15), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")")
|
||||
float GetRadius() const;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -518,7 +518,7 @@ namespace
|
||||
Vector3 nextPos = transform.LocalToWorld(next->Value.Translation);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, NodeSizeByDistance(nextPos, scaleByDistance)), color, 0.0f, depthTest);
|
||||
const float d = (next->Time - prev->Time) / 3.0f;
|
||||
DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest);
|
||||
DEBUG_DRAW_BEZIER(prevPos, transform.LocalToWorld(prev->Value.Translation + prev->TangentOut.Translation * d), transform.LocalToWorld(next->Value.Translation + next->TangentIn.Translation * d), nextPos, color, 0.0f, depthTest);
|
||||
prev = next;
|
||||
prevPos = nextPos;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
// Internal version number of networking implementation. Updated once engine changes serialization or connection rules.
|
||||
#define NETWORK_PROTOCOL_VERSION 4
|
||||
#define NETWORK_PROTOCOL_VERSION 5
|
||||
|
||||
// Enables encoding object ids and typenames via uint32 keys rather than full data send.
|
||||
#define USE_NETWORK_KEYS 1
|
||||
@@ -29,6 +29,7 @@ enum class NetworkMessageIDs : uint8
|
||||
ObjectDespawn,
|
||||
ObjectRole,
|
||||
ObjectRpc,
|
||||
ObjectRpcPart,
|
||||
|
||||
MAX,
|
||||
};
|
||||
@@ -48,6 +49,7 @@ public:
|
||||
static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
static void OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
static void OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
static void OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
|
||||
|
||||
@@ -391,6 +391,7 @@ namespace
|
||||
NetworkInternal::OnNetworkMessageObjectDespawn,
|
||||
NetworkInternal::OnNetworkMessageObjectRole,
|
||||
NetworkInternal::OnNetworkMessageObjectRpc,
|
||||
NetworkInternal::OnNetworkMessageObjectRpcPart,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -55,14 +55,14 @@ PACK_STRUCT(struct NetworkMessageObjectReplicate
|
||||
uint32 OwnerFrame;
|
||||
});
|
||||
|
||||
PACK_STRUCT(struct NetworkMessageObjectReplicatePayload
|
||||
PACK_STRUCT(struct NetworkMessageObjectPartPayload
|
||||
{
|
||||
uint16 DataSize;
|
||||
uint16 PartsCount;
|
||||
uint16 PartSize;
|
||||
});
|
||||
|
||||
PACK_STRUCT(struct NetworkMessageObjectReplicatePart
|
||||
PACK_STRUCT(struct NetworkMessageObjectPart
|
||||
{
|
||||
NetworkMessageIDs ID = NetworkMessageIDs::ObjectReplicatePart;
|
||||
uint32 OwnerFrame;
|
||||
@@ -111,7 +111,7 @@ PACK_STRUCT(struct NetworkMessageObjectRole
|
||||
PACK_STRUCT(struct NetworkMessageObjectRpc
|
||||
{
|
||||
NetworkMessageIDs ID = NetworkMessageIDs::ObjectRpc;
|
||||
uint16 ArgsSize;
|
||||
uint32 OwnerFrame;
|
||||
});
|
||||
|
||||
struct NetworkReplicatedObject
|
||||
@@ -182,13 +182,14 @@ struct Serializer
|
||||
void* Tags[2];
|
||||
};
|
||||
|
||||
struct ReplicateItem
|
||||
struct PartsItem
|
||||
{
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
Guid ObjectId;
|
||||
uint16 PartsLeft;
|
||||
uint32 OwnerFrame;
|
||||
uint32 OwnerClientId;
|
||||
const void* Tag;
|
||||
Array<byte> Data;
|
||||
};
|
||||
|
||||
@@ -220,7 +221,7 @@ struct DespawnItem
|
||||
DataContainer<uint32> Targets;
|
||||
};
|
||||
|
||||
struct RpcItem
|
||||
struct RpcSendItem
|
||||
{
|
||||
ScriptingObjectReference<ScriptingObject> Object;
|
||||
NetworkRpcName Name;
|
||||
@@ -233,11 +234,12 @@ namespace
|
||||
{
|
||||
CriticalSection ObjectsLock;
|
||||
HashSet<NetworkReplicatedObject> Objects;
|
||||
Array<ReplicateItem> ReplicationParts;
|
||||
Array<PartsItem> ReplicationParts;
|
||||
Array<PartsItem> RpcParts;
|
||||
Array<SpawnItemParts> SpawnParts;
|
||||
Array<SpawnItem> SpawnQueue;
|
||||
Array<DespawnItem> DespawnQueue;
|
||||
Array<RpcItem> RpcQueue;
|
||||
Array<RpcSendItem> RpcQueue;
|
||||
Dictionary<Guid, Guid> IdsRemappingTable;
|
||||
NetworkStream* CachedWriteStream = nullptr;
|
||||
NetworkStream* CachedReadStream = nullptr;
|
||||
@@ -251,6 +253,7 @@ namespace
|
||||
#endif
|
||||
Array<Guid> DespawnedObjects;
|
||||
uint32 SpawnId = 0;
|
||||
uint32 RpcId = 0;
|
||||
|
||||
#if USE_EDITOR
|
||||
void OnScriptsReloading()
|
||||
@@ -505,6 +508,76 @@ void SetupObjectSpawnMessageItem(SpawnItem* e, NetworkMessage& msg)
|
||||
msg.WriteStructure(msgDataItem);
|
||||
}
|
||||
|
||||
void SendInParts(NetworkPeer* peer, NetworkChannelType channel, const byte* data, const uint16 dataSize, NetworkMessage& msg, const NetworkRpcName& name, bool toServer, const Guid& objectId, uint32 ownerFrame, NetworkMessageIDs partId)
|
||||
{
|
||||
NetworkMessageObjectPartPayload msgDataPayload;
|
||||
msgDataPayload.DataSize = dataSize;
|
||||
const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid);
|
||||
const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectPartPayload);
|
||||
const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectPart) - networkKeyIdWorstCaseSize;
|
||||
uint32 partsCount = 1;
|
||||
uint32 dataStart = 0;
|
||||
uint32 msgDataSize = dataSize;
|
||||
if (dataSize > msgMaxData)
|
||||
{
|
||||
// Send msgMaxData within first message
|
||||
msgDataSize = msgMaxData;
|
||||
dataStart += msgMaxData;
|
||||
|
||||
// Send rest of the data in separate parts
|
||||
partsCount += Math::DivideAndRoundUp(dataSize - dataStart, partMaxData);
|
||||
|
||||
// TODO: promote channel to Ordered when using parts?
|
||||
}
|
||||
else
|
||||
dataStart += dataSize;
|
||||
ASSERT(partsCount <= MAX_uint8);
|
||||
msgDataPayload.PartsCount = partsCount;
|
||||
msgDataPayload.PartSize = msgDataSize;
|
||||
msg.WriteStructure(msgDataPayload);
|
||||
msg.WriteBytes(data, msgDataSize);
|
||||
uint32 messageSize = msg.Length;
|
||||
if (toServer)
|
||||
peer->EndSendMessage(channel, msg);
|
||||
else
|
||||
peer->EndSendMessage(channel, msg, CachedTargets);
|
||||
|
||||
// Send all other parts
|
||||
for (uint32 partIndex = 1; partIndex < partsCount; partIndex++)
|
||||
{
|
||||
NetworkMessageObjectPart msgDataPart;
|
||||
msgDataPart.ID = partId;
|
||||
msgDataPart.OwnerFrame = ownerFrame;
|
||||
msgDataPart.DataSize = msgDataPayload.DataSize;
|
||||
msgDataPart.PartsCount = msgDataPayload.PartsCount;
|
||||
msgDataPart.PartStart = dataStart;
|
||||
msgDataPart.PartSize = Math::Min(dataSize - dataStart, partMaxData);
|
||||
msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgDataPart);
|
||||
msg.WriteNetworkId(objectId);
|
||||
msg.WriteBytes(data + msgDataPart.PartStart, msgDataPart.PartSize);
|
||||
messageSize += msg.Length;
|
||||
dataStart += msgDataPart.PartSize;
|
||||
if (toServer)
|
||||
peer->EndSendMessage(channel, msg);
|
||||
else
|
||||
peer->EndSendMessage(channel, msg, CachedTargets);
|
||||
}
|
||||
ASSERT_LOW_LAYER(dataStart == dataSize);
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
// Network stats recording
|
||||
if (NetworkInternal::EnableProfiling)
|
||||
{
|
||||
auto& profileEvent = NetworkInternal::ProfilerEvents[name];
|
||||
profileEvent.Count++;
|
||||
profileEvent.DataSize += dataSize;
|
||||
profileEvent.MessageSize += messageSize;
|
||||
profileEvent.Receivers += toServer ? 1 : CachedTargets.Count();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SendObjectSpawnMessage(const SpawnGroup& group, const Array<NetworkClient*>& clients)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
@@ -589,7 +662,7 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli
|
||||
msg.WriteNetworkId(objectId);
|
||||
if (NetworkManager::IsClient())
|
||||
{
|
||||
NetworkManager::Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -598,6 +671,160 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli
|
||||
}
|
||||
}
|
||||
|
||||
void SendDespawn(DespawnItem& e)
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString());
|
||||
NetworkMessageObjectDespawn msgData;
|
||||
Guid objectId = e.Id;
|
||||
{
|
||||
// Remap local client object ids into server ids
|
||||
IdsRemappingTable.KeyOf(objectId, &objectId);
|
||||
}
|
||||
auto peer = NetworkManager::Peer;
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteNetworkId(objectId);
|
||||
BuildCachedTargets(NetworkManager::Clients, e.Targets);
|
||||
if (NetworkManager::IsClient())
|
||||
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||
else
|
||||
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets);
|
||||
}
|
||||
|
||||
void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
|
||||
{
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it.IsEnd())
|
||||
return;
|
||||
auto& item = it->Item;
|
||||
const bool isClient = NetworkManager::IsClient();
|
||||
|
||||
// Skip serialization of objects that none will receive
|
||||
if (!isClient)
|
||||
{
|
||||
BuildCachedTargets(item, targetClients);
|
||||
if (CachedTargets.Count() == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.AsNetworkObject)
|
||||
item.AsNetworkObject->OnNetworkSerialize();
|
||||
|
||||
// Serialize object
|
||||
NetworkStream* stream = CachedWriteStream;
|
||||
stream->Initialize();
|
||||
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true);
|
||||
if (failed)
|
||||
{
|
||||
//NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString());
|
||||
return;
|
||||
}
|
||||
const uint32 size = stream->GetPosition();
|
||||
if (size > MAX_uint16)
|
||||
{
|
||||
LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16);
|
||||
return;
|
||||
}
|
||||
|
||||
#if USE_NETWORK_REPLICATOR_CACHE
|
||||
// Process replication cache to skip sending object data if it didn't change
|
||||
if (item.RepCache.Data.Length() == size &&
|
||||
item.RepCache.Mask == targetClients &&
|
||||
Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
item.RepCache.Mask = targetClients;
|
||||
item.RepCache.Data.Copy(stream->GetBuffer(), size);
|
||||
#endif
|
||||
// TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state)
|
||||
constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable;
|
||||
|
||||
// Send object to clients
|
||||
NetworkMessageObjectReplicate msgData;
|
||||
msgData.OwnerFrame = NetworkManager::Frame;
|
||||
Guid objectId = item.ObjectId, parentId = item.ParentId;
|
||||
{
|
||||
// Remap local client object ids into server ids
|
||||
IdsRemappingTable.KeyOf(objectId, &objectId);
|
||||
IdsRemappingTable.KeyOf(parentId, &parentId);
|
||||
}
|
||||
NetworkPeer* peer = NetworkManager::Peer;
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteNetworkId(objectId);
|
||||
msg.WriteNetworkId(parentId);
|
||||
msg.WriteNetworkName(obj->GetType().Fullname);
|
||||
const NetworkRpcName name(obj->GetTypeHandle(), StringAnsiView::Empty);
|
||||
SendInParts(peer, repChannel, stream->GetBuffer(), size, msg, name, isClient, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectReplicatePart);
|
||||
}
|
||||
|
||||
void SendRpc(RpcSendItem& e)
|
||||
{
|
||||
ScriptingObject* obj = e.Object.Get();
|
||||
if (!obj)
|
||||
return;
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it == Objects.End())
|
||||
{
|
||||
#if !BUILD_RELEASE
|
||||
if (!DespawnedObjects.Contains(obj->GetID()))
|
||||
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
auto& item = it->Item;
|
||||
if (e.ArgsData.Length() > MAX_uint16)
|
||||
{
|
||||
LOG(Error, "Too much data for object RPC method '{}.{}' on object '{}' ({} bytes provided while limit is {}).", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID(), e.ArgsData.Length(), MAX_uint16);
|
||||
return;
|
||||
}
|
||||
const NetworkManagerMode mode = NetworkManager::Mode;
|
||||
NetworkPeer* peer = NetworkManager::Peer;
|
||||
|
||||
bool toServer;
|
||||
if (e.Info.Server && mode == NetworkManagerMode::Client)
|
||||
{
|
||||
// Client -> Server
|
||||
#if USE_NETWORK_REPLICATOR_LOG
|
||||
if (e.Targets.Length() != 0)
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}.{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString());
|
||||
#endif
|
||||
toServer = true;
|
||||
}
|
||||
else if (e.Info.Client && (mode == NetworkManagerMode::Server || mode == NetworkManagerMode::Host))
|
||||
{
|
||||
// Server -> Client(s)
|
||||
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId);
|
||||
if (CachedTargets.IsEmpty())
|
||||
return;
|
||||
toServer = false;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
// Send RPC message
|
||||
//NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}.{} object ID={}", e.Name.First.ToString(), e.Name.Second.ToString(), item.ToString());
|
||||
NetworkMessageObjectRpc msgData;
|
||||
msgData.OwnerFrame = ++RpcId;
|
||||
Guid objectId = item.ObjectId;
|
||||
Guid parentId = item.ParentId;
|
||||
{
|
||||
// Remap local client object ids into server ids
|
||||
IdsRemappingTable.KeyOf(objectId, &objectId);
|
||||
IdsRemappingTable.KeyOf(parentId, &parentId);
|
||||
}
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteNetworkId(objectId);
|
||||
msg.WriteNetworkId(parentId);
|
||||
msg.WriteNetworkName(obj->GetType().Fullname);
|
||||
msg.WriteNetworkName(e.Name.First.GetType().Fullname);
|
||||
msg.WriteNetworkName(e.Name.Second);
|
||||
NetworkChannelType channel = (NetworkChannelType)e.Info.Channel;
|
||||
SendInParts(peer, channel, e.ArgsData.Get(), e.ArgsData.Length(), msg, e.Name, toServer, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectRpcPart);
|
||||
}
|
||||
|
||||
void DeleteNetworkObject(ScriptingObject* obj)
|
||||
{
|
||||
// Remove from the mapping table
|
||||
@@ -708,38 +935,43 @@ FORCE_INLINE void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject
|
||||
Hierarchy->DirtyObject(obj);
|
||||
}
|
||||
|
||||
ReplicateItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
|
||||
PartsItem* AddPartsItem(Array<PartsItem>& items, NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
|
||||
{
|
||||
// Reuse or add part item
|
||||
ReplicateItem* replicateItem = nullptr;
|
||||
for (auto& e : ReplicationParts)
|
||||
PartsItem* item = nullptr;
|
||||
for (auto& e : items)
|
||||
{
|
||||
if (e.OwnerFrame == ownerFrame && e.Data.Count() == dataSize && e.ObjectId == objectId)
|
||||
{
|
||||
// Reuse
|
||||
replicateItem = &e;
|
||||
item = &e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!replicateItem)
|
||||
if (!item)
|
||||
{
|
||||
// Add
|
||||
replicateItem = &ReplicationParts.AddOne();
|
||||
replicateItem->ObjectId = objectId;
|
||||
replicateItem->PartsLeft = partsCount;
|
||||
replicateItem->OwnerFrame = ownerFrame;
|
||||
replicateItem->OwnerClientId = senderClientId;
|
||||
replicateItem->Data.Resize(dataSize);
|
||||
item = &items.AddOne();
|
||||
item->ObjectId = objectId;
|
||||
item->PartsLeft = partsCount;
|
||||
item->OwnerFrame = ownerFrame;
|
||||
item->OwnerClientId = senderClientId;
|
||||
item->Data.Resize(dataSize);
|
||||
}
|
||||
|
||||
// Copy part data
|
||||
ASSERT(replicateItem->PartsLeft > 0);
|
||||
replicateItem->PartsLeft--;
|
||||
ASSERT(partStart + partSize <= replicateItem->Data.Count());
|
||||
ASSERT(item->PartsLeft > 0);
|
||||
item->PartsLeft--;
|
||||
ASSERT(partStart + partSize <= item->Data.Count());
|
||||
const void* partData = event.Message.SkipBytes(partSize);
|
||||
Platform::MemoryCopy(replicateItem->Data.Get() + partStart, partData, partSize);
|
||||
Platform::MemoryCopy(item->Data.Get() + partStart, partData, partSize);
|
||||
|
||||
return replicateItem;
|
||||
return item;
|
||||
}
|
||||
|
||||
FORCE_INLINE PartsItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
|
||||
{
|
||||
return AddPartsItem(ReplicationParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId);
|
||||
}
|
||||
|
||||
void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, byte* data, uint32 dataSize, uint32 senderClientId)
|
||||
@@ -787,6 +1019,24 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b
|
||||
DirtyObjectImpl(item, obj);
|
||||
}
|
||||
|
||||
FORCE_INLINE PartsItem* AddObjectRpcItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
|
||||
{
|
||||
return AddPartsItem(RpcParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId);
|
||||
}
|
||||
|
||||
void InvokeObjectRpc(const NetworkRpcInfo* info, byte* data, uint32 dataSize, uint32 senderClientId, ScriptingObject* obj)
|
||||
{
|
||||
// Setup message reading stream
|
||||
if (CachedReadStream == nullptr)
|
||||
CachedReadStream = New<NetworkStream>();
|
||||
NetworkStream* stream = CachedReadStream;
|
||||
stream->SenderId = senderClientId;
|
||||
stream->Initialize(data, dataSize);
|
||||
|
||||
// Execute RPC
|
||||
info->Execute(obj, stream, info->Tag);
|
||||
}
|
||||
|
||||
void InvokeObjectSpawn(const NetworkMessageObjectSpawn& msgData, const Guid& prefabId, const NetworkMessageObjectSpawnItem* msgDataItems)
|
||||
{
|
||||
ScopeLock lock(ObjectsLock);
|
||||
@@ -1652,9 +1902,6 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
if (Objects.Count() == 0)
|
||||
return;
|
||||
const bool isClient = NetworkManager::IsClient();
|
||||
const bool isServer = NetworkManager::IsServer();
|
||||
const bool isHost = NetworkManager::IsHost();
|
||||
NetworkPeer* peer = NetworkManager::Peer;
|
||||
|
||||
if (!isClient && NewClients.Count() != 0)
|
||||
{
|
||||
@@ -1694,22 +1941,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
PROFILE_CPU_NAMED("DespawnQueue");
|
||||
for (DespawnItem& e : DespawnQueue)
|
||||
{
|
||||
// Send despawn message
|
||||
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString());
|
||||
NetworkMessageObjectDespawn msgData;
|
||||
Guid objectId = e.Id;
|
||||
{
|
||||
// Remap local client object ids into server ids
|
||||
IdsRemappingTable.KeyOf(objectId, &objectId);
|
||||
}
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteNetworkId(objectId);
|
||||
BuildCachedTargets(NetworkManager::Clients, e.Targets);
|
||||
if (isClient)
|
||||
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
|
||||
else
|
||||
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets);
|
||||
SendDespawn(e);
|
||||
}
|
||||
DespawnQueue.Clear();
|
||||
}
|
||||
@@ -1830,6 +2062,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove items from RpcParts after some TTL to reduce memory usage
|
||||
// TODO: remove items from SpawnParts after some TTL to reduce memory usage
|
||||
|
||||
// Replicate all owned networked objects with other clients or server
|
||||
@@ -1871,136 +2104,11 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
PROFILE_CPU_NAMED("Replication");
|
||||
if (CachedWriteStream == nullptr)
|
||||
CachedWriteStream = New<NetworkStream>();
|
||||
NetworkStream* stream = CachedWriteStream;
|
||||
stream->SenderId = NetworkManager::LocalClientId;
|
||||
CachedWriteStream->SenderId = NetworkManager::LocalClientId;
|
||||
// TODO: use Job System when replicated objects count is large
|
||||
for (auto& e : CachedReplicationResult->_entries)
|
||||
{
|
||||
ScriptingObject* obj = e.Object;
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it.IsEnd())
|
||||
continue;
|
||||
auto& item = it->Item;
|
||||
|
||||
// Skip serialization of objects that none will receive
|
||||
if (!isClient)
|
||||
{
|
||||
BuildCachedTargets(item, e.TargetClients);
|
||||
if (CachedTargets.Count() == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.AsNetworkObject)
|
||||
item.AsNetworkObject->OnNetworkSerialize();
|
||||
|
||||
// Serialize object
|
||||
stream->Initialize();
|
||||
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true);
|
||||
if (failed)
|
||||
{
|
||||
//NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString());
|
||||
continue;
|
||||
}
|
||||
const uint32 size = stream->GetPosition();
|
||||
if (size > MAX_uint16)
|
||||
{
|
||||
LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if USE_NETWORK_REPLICATOR_CACHE
|
||||
// Process replication cache to skip sending object data if it didn't change
|
||||
if (item.RepCache.Data.Length() == size &&
|
||||
item.RepCache.Mask == e.TargetClients &&
|
||||
Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.RepCache.Mask = e.TargetClients;
|
||||
item.RepCache.Data.Copy(stream->GetBuffer(), size);
|
||||
#endif
|
||||
// TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state)
|
||||
constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable;
|
||||
|
||||
// Send object to clients
|
||||
NetworkMessageObjectReplicate msgData;
|
||||
msgData.OwnerFrame = NetworkManager::Frame;
|
||||
Guid objectId = item.ObjectId, parentId = item.ParentId;
|
||||
{
|
||||
// Remap local client object ids into server ids
|
||||
IdsRemappingTable.KeyOf(objectId, &objectId);
|
||||
IdsRemappingTable.KeyOf(parentId, &parentId);
|
||||
}
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteNetworkId(objectId);
|
||||
msg.WriteNetworkId(parentId);
|
||||
msg.WriteNetworkName(obj->GetType().Fullname);
|
||||
NetworkMessageObjectReplicatePayload msgDataPayload;
|
||||
msgDataPayload.DataSize = size;
|
||||
const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid);
|
||||
const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectReplicatePayload);
|
||||
const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart) - networkKeyIdWorstCaseSize;
|
||||
uint32 partsCount = 1;
|
||||
uint32 dataStart = 0;
|
||||
uint32 msgDataSize = size;
|
||||
if (size > msgMaxData)
|
||||
{
|
||||
// Send msgMaxData within first message
|
||||
msgDataSize = msgMaxData;
|
||||
dataStart += msgMaxData;
|
||||
|
||||
// Send rest of the data in separate parts
|
||||
partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData);
|
||||
}
|
||||
else
|
||||
dataStart += size;
|
||||
ASSERT(partsCount <= MAX_uint8);
|
||||
msgDataPayload.PartsCount = partsCount;
|
||||
msgDataPayload.PartSize = msgDataSize;
|
||||
msg.WriteStructure(msgDataPayload);
|
||||
msg.WriteBytes(stream->GetBuffer(), msgDataSize);
|
||||
uint32 dataSize = msgDataSize, messageSize = msg.Length;
|
||||
if (isClient)
|
||||
peer->EndSendMessage(repChannel, msg);
|
||||
else
|
||||
peer->EndSendMessage(repChannel, msg, CachedTargets);
|
||||
|
||||
// Send all other parts
|
||||
for (uint32 partIndex = 1; partIndex < partsCount; partIndex++)
|
||||
{
|
||||
NetworkMessageObjectReplicatePart msgDataPart;
|
||||
msgDataPart.OwnerFrame = msgData.OwnerFrame;
|
||||
msgDataPart.DataSize = msgDataPayload.DataSize;
|
||||
msgDataPart.PartsCount = msgDataPayload.PartsCount;
|
||||
msgDataPart.PartStart = dataStart;
|
||||
msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData);
|
||||
msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgDataPart);
|
||||
msg.WriteNetworkId(objectId);
|
||||
msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize);
|
||||
messageSize += msg.Length;
|
||||
dataSize += msgDataPart.PartSize;
|
||||
dataStart += msgDataPart.PartSize;
|
||||
if (isClient)
|
||||
peer->EndSendMessage(repChannel, msg);
|
||||
else
|
||||
peer->EndSendMessage(repChannel, msg, CachedTargets);
|
||||
}
|
||||
ASSERT_LOW_LAYER(dataStart == size);
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
// Network stats recording
|
||||
if (EnableProfiling)
|
||||
{
|
||||
const Pair<ScriptingTypeHandle, StringAnsiView> name(obj->GetTypeHandle(), StringAnsiView::Empty);
|
||||
auto& profileEvent = ProfilerEvents[name];
|
||||
profileEvent.Count++;
|
||||
profileEvent.DataSize += dataSize;
|
||||
profileEvent.MessageSize += messageSize;
|
||||
profileEvent.Receivers += isClient ? 1 : CachedTargets.Count();
|
||||
}
|
||||
#endif
|
||||
SendReplication(e.Object, e.TargetClients);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2009,70 +2117,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
|
||||
PROFILE_CPU_NAMED("Rpc");
|
||||
for (auto& e : RpcQueue)
|
||||
{
|
||||
ScriptingObject* obj = e.Object.Get();
|
||||
if (!obj)
|
||||
continue;
|
||||
auto it = Objects.Find(obj->GetID());
|
||||
if (it == Objects.End())
|
||||
{
|
||||
#if USE_EDITOR || !BUILD_RELEASE
|
||||
if (!DespawnedObjects.Contains(obj->GetID()))
|
||||
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
auto& item = it->Item;
|
||||
|
||||
// Send RPC message
|
||||
//NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString());
|
||||
NetworkMessageObjectRpc msgData;
|
||||
Guid msgObjectId = item.ObjectId;
|
||||
Guid msgParentId = item.ParentId;
|
||||
{
|
||||
// Remap local client object ids into server ids
|
||||
IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId);
|
||||
IdsRemappingTable.KeyOf(msgParentId, &msgParentId);
|
||||
}
|
||||
msgData.ArgsSize = (uint16)e.ArgsData.Length();
|
||||
NetworkMessage msg = peer->BeginSendMessage();
|
||||
msg.WriteStructure(msgData);
|
||||
msg.WriteNetworkId(msgObjectId);
|
||||
msg.WriteNetworkId(msgParentId);
|
||||
msg.WriteNetworkName(obj->GetType().Fullname);
|
||||
msg.WriteNetworkName(e.Name.First.GetType().Fullname);
|
||||
msg.WriteNetworkName(e.Name.Second);
|
||||
msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length());
|
||||
uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0;
|
||||
NetworkChannelType channel = (NetworkChannelType)e.Info.Channel;
|
||||
if (e.Info.Server && isClient)
|
||||
{
|
||||
// Client -> Server
|
||||
#if USE_NETWORK_REPLICATOR_LOG
|
||||
if (e.Targets.Length() != 0)
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString());
|
||||
#endif
|
||||
peer->EndSendMessage(channel, msg);
|
||||
receivers = 1;
|
||||
}
|
||||
else if (e.Info.Client && (isServer || isHost))
|
||||
{
|
||||
// Server -> Client(s)
|
||||
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId);
|
||||
peer->EndSendMessage(channel, msg, CachedTargets);
|
||||
receivers = CachedTargets.Count();
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
// Network stats recording
|
||||
if (EnableProfiling && receivers)
|
||||
{
|
||||
auto& profileEvent = ProfilerEvents[e.Name];
|
||||
profileEvent.Count++;
|
||||
profileEvent.DataSize += dataSize;
|
||||
profileEvent.MessageSize += messageSize;
|
||||
profileEvent.Receivers += receivers;
|
||||
}
|
||||
#endif
|
||||
SendRpc(e);
|
||||
}
|
||||
RpcQueue.Clear();
|
||||
}
|
||||
@@ -2085,7 +2130,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
|
||||
{
|
||||
PROFILE_CPU();
|
||||
NetworkMessageObjectReplicate msgData;
|
||||
NetworkMessageObjectReplicatePayload msgDataPayload;
|
||||
NetworkMessageObjectPartPayload msgDataPayload;
|
||||
Guid objectId, parentId;
|
||||
StringAnsiView objectTypeName;
|
||||
event.Message.ReadStructure(msgData);
|
||||
@@ -2095,7 +2140,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
|
||||
event.Message.ReadStructure(msgDataPayload);
|
||||
ScopeLock lock(ObjectsLock);
|
||||
if (DespawnedObjects.Contains(objectId))
|
||||
return; // Skip replicating not-existing objects
|
||||
return; // Skip replicating non-existing objects
|
||||
NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName);
|
||||
if (!e)
|
||||
return;
|
||||
@@ -2114,7 +2159,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
|
||||
else
|
||||
{
|
||||
// Add to replication from multiple parts
|
||||
ReplicateItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId);
|
||||
PartsItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId);
|
||||
replicateItem->Object = e->Object;
|
||||
}
|
||||
}
|
||||
@@ -2122,13 +2167,13 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
|
||||
void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
NetworkMessageObjectReplicatePart msgData;
|
||||
NetworkMessageObjectPart msgData;
|
||||
Guid objectId;
|
||||
event.Message.ReadStructure(msgData);
|
||||
event.Message.ReadNetworkId(objectId);
|
||||
ScopeLock lock(ObjectsLock);
|
||||
if (DespawnedObjects.Contains(objectId))
|
||||
return; // Skip replicating not-existing objects
|
||||
return; // Skip replicating non-existing objects
|
||||
|
||||
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
|
||||
AddObjectReplicateItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId);
|
||||
@@ -2288,14 +2333,16 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
|
||||
{
|
||||
PROFILE_CPU();
|
||||
NetworkMessageObjectRpc msgData;
|
||||
Guid msgObjectId, msgParentId;
|
||||
NetworkMessageObjectPartPayload msgDataPayload;
|
||||
Guid objectId, parentId;
|
||||
StringAnsiView objectTypeName, rpcTypeName, rpcName;
|
||||
event.Message.ReadStructure(msgData);
|
||||
event.Message.ReadNetworkId(msgObjectId);
|
||||
event.Message.ReadNetworkId(msgParentId);
|
||||
event.Message.ReadNetworkId(objectId);
|
||||
event.Message.ReadNetworkId(parentId);
|
||||
event.Message.ReadNetworkName(objectTypeName);
|
||||
event.Message.ReadNetworkName(rpcTypeName);
|
||||
event.Message.ReadNetworkName(rpcName);
|
||||
event.Message.ReadStructure(msgDataPayload);
|
||||
ScopeLock lock(ObjectsLock);
|
||||
|
||||
// Find RPC info
|
||||
@@ -2305,11 +2352,11 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
|
||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name);
|
||||
if (!info)
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), msgObjectId);
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), objectId);
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkReplicatedObject* e = ResolveObject(msgObjectId, msgParentId, objectTypeName);
|
||||
NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName);
|
||||
if (e)
|
||||
{
|
||||
auto& item = *e;
|
||||
@@ -2329,18 +2376,50 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup message reading stream
|
||||
if (CachedReadStream == nullptr)
|
||||
CachedReadStream = New<NetworkStream>();
|
||||
NetworkStream* stream = CachedReadStream;
|
||||
stream->SenderId = client ? client->ClientId : NetworkManager::ServerClientId;
|
||||
stream->Initialize(event.Message.Buffer + event.Message.Position, msgData.ArgsSize);
|
||||
|
||||
// Execute RPC
|
||||
info->Execute(obj, stream, info->Tag);
|
||||
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
|
||||
if (msgDataPayload.PartsCount == 1)
|
||||
{
|
||||
// Call RPC
|
||||
InvokeObjectRpc(info, event.Message.Buffer + event.Message.Position, msgDataPayload.DataSize, senderClientId, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add to RPC from multiple parts
|
||||
PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId);
|
||||
rpcItem->Object = e->Object;
|
||||
rpcItem->Tag = info;
|
||||
}
|
||||
}
|
||||
else if (info->Channel != static_cast<uint8>(NetworkChannelType::Unreliable) && info->Channel != static_cast<uint8>(NetworkChannelType::UnreliableOrdered))
|
||||
{
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgObjectId, String(rpcTypeName), String(rpcName));
|
||||
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", objectId, String(rpcTypeName), String(rpcName));
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkInternal::OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
NetworkMessageObjectPart msgData;
|
||||
Guid objectId;
|
||||
event.Message.ReadStructure(msgData);
|
||||
event.Message.ReadNetworkId(objectId);
|
||||
ScopeLock lock(ObjectsLock);
|
||||
if (DespawnedObjects.Contains(objectId))
|
||||
return; // Skip replicating non-existing objects
|
||||
|
||||
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
|
||||
PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId);
|
||||
if (rpcItem && rpcItem->PartsLeft == 0)
|
||||
{
|
||||
// Got all parts so invoke RPC
|
||||
ScriptingObject* obj = rpcItem->Object.Get();
|
||||
if (obj)
|
||||
{
|
||||
InvokeObjectRpc((const NetworkRpcInfo*)rpcItem->Tag, rpcItem->Data.Get(), rpcItem->Data.Count(), rpcItem->OwnerClientId, obj);
|
||||
}
|
||||
|
||||
// Remove item
|
||||
int32 partIndex = (int32)((RpcParts.Get() - rpcItem) / sizeof(rpcItem));
|
||||
RpcParts.RemoveAt(partIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,8 +425,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
|
||||
case 300:
|
||||
{
|
||||
// Load function asset
|
||||
const auto function = Assets.LoadAsync<ParticleEmitterFunction>((Guid)node->Values[0]);
|
||||
if (!function || function->WaitForLoaded())
|
||||
const auto function = Assets.Load<ParticleEmitterFunction>((Guid)node->Values[0]);
|
||||
if (!function)
|
||||
{
|
||||
OnError(node, box, TEXT("Missing or invalid function."));
|
||||
value = Value::Zero;
|
||||
@@ -439,7 +439,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va
|
||||
{
|
||||
if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300))
|
||||
{
|
||||
const auto callFunc = Assets.LoadAsync<ParticleEmitterFunction>((Guid)_callStack[i]->Values[0]);
|
||||
const auto callFunc = Assets.Load<ParticleEmitterFunction>((Guid)_callStack[i]->Values[0]);
|
||||
if (callFunc == function)
|
||||
{
|
||||
OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString()));
|
||||
@@ -514,7 +514,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupFunction(Box* box, Node* node, Val
|
||||
value = Value::Zero;
|
||||
break;
|
||||
}
|
||||
const auto function = Assets.LoadAsync<ParticleEmitterFunction>((Guid)functionCallNode->Values[0]);
|
||||
const auto function = Assets.Load<ParticleEmitterFunction>((Guid)functionCallNode->Values[0]);
|
||||
if (!_functions.TryGet(functionCallNode, graph) || !function)
|
||||
{
|
||||
OnError(node, box, TEXT("Missing calling function graph."));
|
||||
|
||||
@@ -156,7 +156,7 @@ void ParticleSystemInstance::Sync(ParticleSystem* system)
|
||||
if (GPUParticlesCountReadback)
|
||||
GPUParticlesCountReadback->ReleaseGPU();
|
||||
}
|
||||
ASSERT(Emitters.Count() == system->Emitters.Count());
|
||||
CHECK(Emitters.Count() == system->Emitters.Count());
|
||||
}
|
||||
|
||||
bool ParticleSystemInstance::ContainsEmitter(ParticleEmitter* emitter) const
|
||||
|
||||
@@ -194,7 +194,6 @@ void Collider::UpdateLayerBits()
|
||||
// Own layer mask
|
||||
const uint32 mask1 = Physics::LayerMasks[GetLayer()];
|
||||
|
||||
ASSERT(_shape);
|
||||
PhysicsBackend::SetShapeFilterMask(_shape, mask0, mask1);
|
||||
}
|
||||
|
||||
@@ -244,10 +243,14 @@ void Collider::UpdateGeometry()
|
||||
if (actor)
|
||||
{
|
||||
const auto rigidBody = dynamic_cast<RigidBody*>(GetParent());
|
||||
if (_staticActor != nullptr || (rigidBody && CanAttach(rigidBody)))
|
||||
if (rigidBody && CanAttach(rigidBody))
|
||||
{
|
||||
Attach(rigidBody);
|
||||
}
|
||||
else if (_staticActor != nullptr)
|
||||
{
|
||||
PhysicsBackend::AttachShape(_shape, actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Be static triangle mesh
|
||||
|
||||
@@ -1969,6 +1969,7 @@ void PhysicsBackend::EndSimulateScene(void* scene)
|
||||
scenePhysX->EventsCallback.SendTriggerEvents();
|
||||
scenePhysX->EventsCallback.SendCollisionEvents();
|
||||
scenePhysX->EventsCallback.SendJointEvents();
|
||||
scenePhysX->EventsCallback.Clear();
|
||||
}
|
||||
|
||||
// Clear delta after simulation ended
|
||||
@@ -4466,14 +4467,14 @@ void PhysicsBackend::FlushRequests(void* scene)
|
||||
}
|
||||
if (scenePhysX->RemoveColliders.HasItems())
|
||||
{
|
||||
for (int32 i = 0; i < scenePhysX->RemoveColliders.Count(); i++)
|
||||
scenePhysX->EventsCallback.OnColliderRemoved(scenePhysX->RemoveColliders[i]);
|
||||
for (PhysicsColliderActor* e : scenePhysX->RemoveColliders)
|
||||
scenePhysX->EventsCallback.OnColliderRemoved(e);
|
||||
scenePhysX->RemoveColliders.Clear();
|
||||
}
|
||||
if (scenePhysX->RemoveJoints.HasItems())
|
||||
{
|
||||
for (int32 i = 0; i < scenePhysX->RemoveJoints.Count(); i++)
|
||||
scenePhysX->EventsCallback.OnJointRemoved(scenePhysX->RemoveJoints[i]);
|
||||
for (Joint* e : scenePhysX->RemoveJoints)
|
||||
scenePhysX->EventsCallback.OnJointRemoved(e);
|
||||
scenePhysX->RemoveJoints.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#if COMPILE_WITH_EMPTY_PHYSICS
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Physics/PhysicsBackend.h"
|
||||
#include "Engine/Physics/CollisionData.h"
|
||||
#include "Engine/Physics/PhysicalMaterial.h"
|
||||
#include "Engine/Physics/PhysicsScene.h"
|
||||
@@ -226,26 +227,6 @@ bool PhysicsBackend::CheckConvex(void* scene, const Vector3& center, const Colli
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicsBackend::OverlapSphere(void* scene, const Vector3& center, const float radius, Array<Collider*>& results, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicsBackend::OverlapCapsule(void* scene, const Vector3& center, const float radius, const float height, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicsBackend::OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<Collider*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
|
||||
{
|
||||
return false;
|
||||
@@ -353,7 +334,7 @@ Vector3 PhysicsBackend::GetRigidDynamicActorCenterOfMass(void* actor)
|
||||
return Vector3::Zero;
|
||||
}
|
||||
|
||||
void PhysicsBackend::SetRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value)
|
||||
void PhysicsBackend::AddRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -698,6 +679,15 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value)
|
||||
{
|
||||
}
|
||||
|
||||
Vector3 PhysicsBackend::GetControllerBasePosition(void* controller)
|
||||
{
|
||||
return Vector3::Zero;
|
||||
}
|
||||
|
||||
void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value)
|
||||
{
|
||||
}
|
||||
|
||||
Vector3 PhysicsBackend::GetControllerUpDirection(void* controller)
|
||||
{
|
||||
return Vector3::Up;
|
||||
|
||||
@@ -543,11 +543,9 @@ void WindowsPlatform::ReleaseMutex()
|
||||
}
|
||||
}
|
||||
|
||||
void WindowsPlatform::PreInit(void* hInstance)
|
||||
PRAGMA_DISABLE_OPTIMIZATION;
|
||||
void CheckInstructionSet()
|
||||
{
|
||||
ASSERT(hInstance);
|
||||
Instance = hInstance;
|
||||
|
||||
#if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64
|
||||
// Check the minimum vector instruction set support
|
||||
int32 cpuInfo[4] = { -1 };
|
||||
@@ -597,10 +595,19 @@ void WindowsPlatform::PreInit(void* hInstance)
|
||||
{
|
||||
// Not supported CPU
|
||||
CPUBrand cpu;
|
||||
Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer)));
|
||||
Platform::Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer)));
|
||||
exit(-1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
PRAGMA_ENABLE_OPTIMIZATION;
|
||||
|
||||
void WindowsPlatform::PreInit(void* hInstance)
|
||||
{
|
||||
ASSERT(hInstance);
|
||||
Instance = hInstance;
|
||||
|
||||
CheckInstructionSet();
|
||||
|
||||
// Disable the process from being showing "ghosted" while not responding messages during slow tasks
|
||||
DisableProcessWindowsGhosting();
|
||||
|
||||
@@ -47,6 +47,9 @@ public:
|
||||
Data CachedData;
|
||||
ToneMappingMode Mode = ToneMappingMode::None;
|
||||
Texture* LutTexture = nullptr;
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
uint64 FrameRendered = 0;
|
||||
#endif
|
||||
|
||||
~ColorGradingCustomBuffer()
|
||||
{
|
||||
@@ -54,6 +57,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
|
||||
void ColorGradingPass::OnShaderReloading(Asset* obj)
|
||||
{
|
||||
_psLut.Release();
|
||||
invalidateResources();
|
||||
_reloadedFrame = Engine::FrameCount;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
String ColorGradingPass::ToString() const
|
||||
{
|
||||
return TEXT("ColorGradingPass");
|
||||
@@ -194,6 +208,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
||||
// Check if LUT parameter hasn't been changed since the last time
|
||||
if (Platform::MemoryCompare(&colorGradingBuffer.CachedData , &data, sizeof(Data)) == 0 &&
|
||||
colorGradingBuffer.Mode == toneMapping.Mode &&
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
colorGradingBuffer.FrameRendered > _reloadedFrame &&
|
||||
#endif
|
||||
Engine::FrameCount > 30 && // Skip caching when engine is starting TODO: find why this hack is needed
|
||||
colorGradingBuffer.LutTexture == lutTexture)
|
||||
{
|
||||
@@ -203,6 +220,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
|
||||
colorGradingBuffer.CachedData = data;
|
||||
colorGradingBuffer.Mode = toneMapping.Mode;
|
||||
colorGradingBuffer.LutTexture = lutTexture;
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
colorGradingBuffer.FrameRendered = Engine::FrameCount;
|
||||
#endif
|
||||
|
||||
// Render LUT
|
||||
PROFILE_GPU("Color Grading LUT");
|
||||
|
||||
@@ -25,11 +25,8 @@ public:
|
||||
|
||||
private:
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
{
|
||||
_psLut.Release();
|
||||
invalidateResources();
|
||||
}
|
||||
uint64 _reloadedFrame = 0;
|
||||
void OnShaderReloading(Asset* obj);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -98,6 +98,7 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu
|
||||
if (mode == EyeAdaptationMode::Manual)
|
||||
{
|
||||
// Apply fixed manual exposure
|
||||
context->ResetSR();
|
||||
context->SetRenderTarget(*colorBuffer);
|
||||
context->SetViewportAndScissors((float)colorBuffer->Width(), (float)colorBuffer->Height());
|
||||
context->SetState(_psManual);
|
||||
|
||||
@@ -30,6 +30,9 @@ GPU_CB_STRUCT(Data {
|
||||
|
||||
Float2 Input0SizeInv;
|
||||
Float2 Input2SizeInv;
|
||||
|
||||
Float3 PrevWorldOriginOffset;
|
||||
float Dummy1;
|
||||
});
|
||||
|
||||
MotionBlurPass::MotionBlurPass()
|
||||
@@ -194,6 +197,7 @@ void MotionBlurPass::RenderMotionVectors(RenderContext& renderContext)
|
||||
Matrix::Transpose(renderContext.View.ViewProjection(), data.CurrentVP);
|
||||
Matrix::Transpose(renderContext.View.PrevViewProjection, data.PreviousVP);
|
||||
data.TemporalAAJitter = renderContext.View.TemporalAAJitter;
|
||||
data.PrevWorldOriginOffset = renderContext.View.Origin - renderContext.View.PrevOrigin;
|
||||
auto cb = _shader->GetShader()->GetCB(0);
|
||||
context->UpdateCB(cb, &data);
|
||||
context->BindCB(0, cb);
|
||||
|
||||
@@ -23,6 +23,10 @@ private:
|
||||
StringAnsiView _fullname;
|
||||
uint32 _types = 0;
|
||||
mutable uint32 _size = 0;
|
||||
#else
|
||||
StringAnsiView _name;
|
||||
StringAnsiView _namespace;
|
||||
StringAnsiView _fullname;
|
||||
#endif
|
||||
MAssembly* _assembly;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
MDomain* MRootDomain = nullptr;
|
||||
MDomain* MActiveDomain = nullptr;
|
||||
Array<MDomain*, FixedAllocation<4>> MDomains;
|
||||
bool MCore::Ready = false;
|
||||
|
||||
MClass* MCore::TypeCache::Void = nullptr;
|
||||
MClass* MCore::TypeCache::Object = nullptr;
|
||||
@@ -301,6 +302,11 @@ bool MProperty::IsStatic() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void MCore::OnManagedEventAfterShutdown(const char* eventName)
|
||||
{
|
||||
LOG(Error, "Found a binding leak on '{}' event used by C# scripting after shutdown. Ensure to unregister scripting events from objects during disposing.", ::String(eventName));
|
||||
}
|
||||
|
||||
MDomain* MCore::GetRootDomain()
|
||||
{
|
||||
return MRootDomain;
|
||||
|
||||
@@ -57,6 +57,10 @@ public:
|
||||
static void UnloadScriptingAssemblyLoadContext();
|
||||
#endif
|
||||
|
||||
// Utility for guarding against using C# scripting runtime after shutdown (eg. when asset delegate is not properly disposed).
|
||||
static bool Ready;
|
||||
static void OnManagedEventAfterShutdown(const char* eventName);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Utilities for C# object management.
|
||||
|
||||
@@ -19,6 +19,8 @@ protected:
|
||||
#elif USE_NETCORE
|
||||
void* _handle;
|
||||
StringAnsiView _name;
|
||||
#else
|
||||
StringAnsiView _name;
|
||||
#endif
|
||||
|
||||
mutable MMethod* _addMethod;
|
||||
|
||||
@@ -24,6 +24,8 @@ protected:
|
||||
void* _type;
|
||||
int32 _fieldOffset;
|
||||
StringAnsiView _name;
|
||||
#else
|
||||
StringAnsiView _name;
|
||||
#endif
|
||||
|
||||
MClass* _parentClass;
|
||||
|
||||
@@ -30,6 +30,8 @@ protected:
|
||||
mutable void* _returnType;
|
||||
mutable Array<void*, InlinedAllocation<8>> _parameterTypes;
|
||||
void CacheSignature() const;
|
||||
#else
|
||||
StringAnsiView _name;
|
||||
#endif
|
||||
MClass* _parentClass;
|
||||
MVisibility _visibility;
|
||||
|
||||
@@ -22,6 +22,8 @@ protected:
|
||||
#elif USE_NETCORE
|
||||
void* _handle;
|
||||
StringAnsiView _name;
|
||||
#else
|
||||
StringAnsiView _name;
|
||||
#endif
|
||||
|
||||
mutable MMethod* _getMethod;
|
||||
|
||||
@@ -316,7 +316,8 @@ bool MCore::LoadEngine()
|
||||
|
||||
char* buildInfo = CallStaticMethod<char*>(GetStaticMethodPointer(TEXT("GetRuntimeInformation")));
|
||||
LOG(Info, ".NET runtime version: {0}", ::String(buildInfo));
|
||||
MCore::GC::FreeMemory(buildInfo);
|
||||
GC::FreeMemory(buildInfo);
|
||||
Ready = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -327,6 +328,7 @@ void MCore::UnloadEngine()
|
||||
return;
|
||||
PROFILE_CPU();
|
||||
CallStaticMethod<void>(GetStaticMethodPointer(TEXT("Exit")));
|
||||
Ready = false;
|
||||
MDomains.ClearDelete();
|
||||
MRootDomain = nullptr;
|
||||
ShutdownHostfxr();
|
||||
@@ -1798,6 +1800,33 @@ bool InitHostfxr()
|
||||
get_hostfxr_params.dotnet_root = dotnetRoot.Get();
|
||||
}
|
||||
#endif
|
||||
String platformStr;
|
||||
switch (PLATFORM_TYPE)
|
||||
{
|
||||
case PlatformType::Windows:
|
||||
case PlatformType::UWP:
|
||||
if (PLATFORM_ARCH == ArchitectureType::x64)
|
||||
platformStr = "Windows x64";
|
||||
else if (PLATFORM_ARCH == ArchitectureType::ARM64)
|
||||
platformStr = "Windows ARM64";
|
||||
else
|
||||
platformStr = "Windows x86";
|
||||
break;
|
||||
case PlatformType::Linux:
|
||||
platformStr = PLATFORM_ARCH_ARM64 ? "Linux ARM64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86";
|
||||
break;
|
||||
case PlatformType::Mac:
|
||||
platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS ARM64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86";
|
||||
break;
|
||||
default:
|
||||
if (PLATFORM_ARCH == ArchitectureType::x64)
|
||||
platformStr = "x64";
|
||||
else if (PLATFORM_ARCH == ArchitectureType::ARM64)
|
||||
platformStr = "ARM64";
|
||||
else
|
||||
platformStr = "x86";
|
||||
}
|
||||
|
||||
char_t hostfxrPath[1024];
|
||||
size_t hostfxrPathSize = sizeof(hostfxrPath) / sizeof(char_t);
|
||||
int rc = get_hostfxr_path(hostfxrPath, &hostfxrPathSize, &get_hostfxr_params);
|
||||
@@ -1810,9 +1839,9 @@ bool InitHostfxr()
|
||||
Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet"));
|
||||
#endif
|
||||
#if USE_EDITOR
|
||||
LOG(Fatal, "Missing .NET 8 or later SDK installation required to run Flax Editor.");
|
||||
LOG(Fatal, "Missing .NET 8 or later SDK installation for {0} is required to run Flax Editor.", platformStr);
|
||||
#else
|
||||
LOG(Fatal, "Missing .NET 8 or later Runtime installation required to run this application.");
|
||||
LOG(Fatal, "Missing .NET 8 or later Runtime installation for {0} is required to run this application.", platformStr);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
@@ -1870,27 +1899,6 @@ bool InitHostfxr()
|
||||
hostfxr_close(handle);
|
||||
if (rc == 0x80008096) // FrameworkMissingFailure
|
||||
{
|
||||
String platformStr;
|
||||
switch (PLATFORM_TYPE)
|
||||
{
|
||||
case PlatformType::Windows:
|
||||
case PlatformType::UWP:
|
||||
if (PLATFORM_ARCH == ArchitectureType::x64)
|
||||
platformStr = "Windows x64";
|
||||
else if (PLATFORM_ARCH == ArchitectureType::ARM64)
|
||||
platformStr = "Windows ARM64";
|
||||
else
|
||||
platformStr = "Windows x86";
|
||||
break;
|
||||
case PlatformType::Linux:
|
||||
platformStr = PLATFORM_ARCH_ARM64 ? "Linux ARM64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86";
|
||||
break;
|
||||
case PlatformType::Mac:
|
||||
platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS ARM64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86";
|
||||
break;
|
||||
default:;
|
||||
platformStr = "";
|
||||
}
|
||||
LOG(Fatal, "Failed to resolve compatible .NET runtime version in '{0}'. Make sure the correct platform version for runtime is installed ({1})", platformStr, String(init_params.dotnet_root));
|
||||
}
|
||||
else
|
||||
@@ -2022,13 +2030,13 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname)
|
||||
String fileName = name;
|
||||
if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe")))
|
||||
fileName += TEXT(".dll");
|
||||
String path = fileName;
|
||||
String path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName;
|
||||
if (!FileSystem::FileExists(path))
|
||||
{
|
||||
path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName;
|
||||
if (!FileSystem::FileExists(path))
|
||||
{
|
||||
path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName;
|
||||
path = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class Scripting : EngineModule
|
||||
options.ScriptingAPI.Defines.Add("NET");
|
||||
AddFrameworkDefines("NET{0}_{1}", dotnetSdk.Version.Major, 0); // "NET7_0"
|
||||
for (int i = 5; i <= dotnetSdk.Version.Major; i++)
|
||||
AddFrameworkDefines("NET{0}_{1}_OR_GREATER", dotnetSdk.Version.Major, 0); // "NET7_0_OR_GREATER"
|
||||
AddFrameworkDefines("NET{0}_{1}_OR_GREATER", i, 0); // "NET7_0_OR_GREATER"
|
||||
options.ScriptingAPI.Defines.Add("NETCOREAPP");
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 3, 1); // "NETCOREAPP3_1_OR_GREATER"
|
||||
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 2, 2);
|
||||
|
||||
@@ -93,6 +93,8 @@ ScriptingObject::ScriptingObject(const SpawnParams& params)
|
||||
: _gcHandle((MGCHandle)params.Managed)
|
||||
#elif !COMPILE_WITHOUT_CSHARP
|
||||
: _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0)
|
||||
#else
|
||||
: _gcHandle(0)
|
||||
#endif
|
||||
, _type(params.Type)
|
||||
, _id(params.ID)
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include "Types.h"
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include "Engine/Core/Types/Guid.h"
|
||||
#if PLATFORM_ARCH_ARM64
|
||||
#include "Engine/Core/Core.h"
|
||||
#endif
|
||||
|
||||
class MMethod;
|
||||
class BinaryModule;
|
||||
@@ -563,8 +566,8 @@ FORCE_INLINE int32 GetVTableIndex(void** vtable, int32 entriesCount, void* func)
|
||||
offset = ((*op & 0x3FFC00) >> 10) * ((*op & 0x40000000) != 0 ? 8 : 4);
|
||||
return offset / sizeof(void*);
|
||||
}
|
||||
CRASH;
|
||||
}
|
||||
CRASH;
|
||||
#elif defined(__clang__)
|
||||
// On Clang member function pointer represents the offset from the vtable begin.
|
||||
return (int32)(intptr)func / sizeof(void*);
|
||||
|
||||
@@ -15,7 +15,6 @@ class MemoryWriteStream;
|
||||
struct FLAXENGINE_API ShaderCompilationOptions
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Name of the target object (name of the shader or material for better logging readability)
|
||||
/// </summary>
|
||||
@@ -37,12 +36,16 @@ public:
|
||||
uint32 SourceLength = 0;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Target shader profile
|
||||
/// </summary>
|
||||
ShaderProfile Profile = ShaderProfile::Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Target platform, set to invalid value of 0 if not used (platform-independent compilation).
|
||||
/// </summary>
|
||||
PlatformType Platform = (PlatformType)0;
|
||||
|
||||
/// <summary>
|
||||
/// Disables shaders compiler optimizations. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.
|
||||
/// </summary>
|
||||
@@ -64,7 +67,6 @@ public:
|
||||
Array<ShaderMacro> Macros;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Output stream to write compiled shader cache to
|
||||
/// </summary>
|
||||
|
||||
@@ -196,7 +196,7 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
if (_profile == ShaderProfile::DirectX_SM5)
|
||||
if (GetProfile() == ShaderProfile::DirectX_SM5)
|
||||
{
|
||||
profileName += "_5_0";
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user